Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.6     ✓ dplyr   1.0.7
✓ tidyr   1.1.4     ✓ stringr 1.4.0
✓ readr   2.1.1     ✓ forcats 0.5.1
── Conflicts ────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::collapse()   masks IRanges::collapse()
x dplyr::combine()    masks BiocGenerics::combine()
x dplyr::desc()       masks IRanges::desc()
x tidyr::expand()     masks S4Vectors::expand()
x dplyr::filter()     masks stats::filter()
x dplyr::first()      masks S4Vectors::first()
x dplyr::lag()        masks stats::lag()
x ggplot2::Position() masks BiocGenerics::Position(), base::Position()
x purrr::reduce()     masks GenomicRanges::reduce(), IRanges::reduce()
x dplyr::rename()     masks S4Vectors::rename()
x dplyr::slice()      masks IRanges::slice()
ggtree v3.0.4  For help: https://yulab-smu.top/treedata-book/

If you use ggtree in published research, please cite the most appropriate paper(s):

1. Guangchuang Yu. Using ggtree to visualize data on tree-like structures. Current Protocols in Bioinformatics, 2020, 69:e96. doi:10.1002/cpbi.96
2. Guangchuang Yu, Tommy Tsan-Yuk Lam, Huachen Zhu, Yi Guan. Two methods for mapping and visualizing associated data on phylogeny using ggtree. Molecular Biology and Evolution 2018, 35(12):3041-3043. doi:10.1093/molbev/msy194
3. Guangchuang Yu, David Smith, Huachen Zhu, Yi Guan, Tommy Tsan-Yuk Lam. ggtree: an R package for visualization and annotation of phylogenetic trees with their covariates and other associated data. Methods in Ecology and Evolution 2017, 8(1):28-36. doi:10.1111/2041-210X.12628



Attaching package: ‘ggtree’

The following object is masked from ‘package:tidyr’:

    expand

The following object is masked from ‘package:IRanges’:

    collapse

The following object is masked from ‘package:S4Vectors’:

    expand

treeio v1.16.2  For help: https://yulab-smu.top/treedata-book/

If you use treeio in published research, please cite:

LG Wang, TTY Lam, S Xu, Z Dai, L Zhou, T Feng, P Guo, CW Dunn, BR Jones, T Bradley, H Zhu, Y Guan, Y Jiang, G Yu. treeio: an R package for phylogenetic tree input and output with richly annotated and associated data. Molecular Biology and Evolution 2020, 37(2):599-603. doi: 10.1093/molbev/msz240

Goal

Analyze the adhesin properties of the XP_028889033 homologs (putative adhesin in C. auris) This is version 3 of the analysis, using the updated homologs on 2021-02-05

Build datasets

Basic information

First get the basic information about the 100 sequences in this study. I decide to write a simple Python script to extract such info.

# edit the PYTHON path below to match your local system
~/sw/miniconda3/bin/python extract_seq_info.py

Load in the sequence information.

note that I need to manually edit the sequence ids for four sequences from fungidb, because I used their refseq_id when retrieving their chromosomal locations.

GRYC ID Refseq_ID
CLUG_05233 XP_002615218.1
CPAR2_600430 XP_036662815.1
CPAR2_806390 XP_036665262.1
CPAR2_806420 XP_036665265.1
sps.list <- c("Cduobushaemulonis","Cpseudohaemulonis","Chaemuloni","Cauris","Clusitaniae","Dhansenii","Cparapsilosis","Lelongisporus","Ctropicalis","Cdubliniensis","Calbicans","Sstipitis","Klactis","Ncastellii","Cglabrata","Nbracarensis","Ndelphensis","Nnivariensis")
seqInfo <- read_tsv("data/XP_028889033_homologs.tsv", comment = "#", col_types = "ccci") %>% 
  mutate(species_id = factor(species, levels = sps.list), species = NULL)
chrLoc <- read_tsv("data/XP_028889033_homologs_chr_loc.tsv", col_types = cols()) %>% 
  mutate(id = ifelse(is.na(accession), gene_id, accession), 
         accession = NULL, 
         species_id = factor(species_id, levels = sps.list),
         relLoc = round(chrstart/chrL, 3))

seqInfo <- seqInfo %>% 
  left_join(chrLoc) %>% 
  mutate(assemblystatus = factor(assemblystatus, levels = c("Complete Genome","Chromosome","Contig","Scaffold"), 
                                 labels = c("Chromosome", "Chromosome", "Partial", "Partial")),
         species_gr = factor(species_id, labels = 
                               c(rep("MDR clade",4),"C. lusitaniae","D. hansenii",rep("C. parapsilosis",2),
                                 rep("albicans clade",3), "S. stipitis", rep("Saccharomycetaceae",6)))) %>% 
  select(name, id, source, starts_with("gene"), starts_with("species"), strain, starts_with("assembly"),
         length, n_seqs, starts_with("chr"), relLoc)
Joining, by = c("id", "species_id")

Adhesin predictions

FungalRV and FaaPred are two Support Vector Machine (SVM) based prediction algorithms that use sequence features such as amino acid composition (frequency, physiochemical properties etc.) as input and train Machine Learning models to distinguish fungal adhesins from non-adhesins.

frv.th = 0.511 # recommended FungalRV score threshold
frv <- read_tsv("output/FungalRV/fungalRV_result.txt", skip = 3, col_names = c("name","frv.score"), col_types = "cd") %>% 
  mutate(name = str_sub(name, 2), frv.pred = frv.score > frv.th)
faa <- read_tsv("output/web-download/faapred_result.txt", col_names = c("name","faa.score","faa.pred"), col_types = "cdc") %>% 
  mutate(faa.pred = ifelse(faa.pred == "Adhesin", TRUE, FALSE))
if("frv.score" %in% names(seqInfo))
  seqInfo <- select(seqInfo, -frv.score, -frv.pred, -faa.score, -faa.pred)
seqInfo <- seqInfo %>% left_join(frv) %>% left_join(faa)
Joining, by = "name"
Joining, by = "name"
df <- seqInfo %>% 
  group_by(species_id) %>% 
  summarize(n = n(), 
            fungalRV = sum(frv.score > 0.511), faapred = sum(faa.pred, na.rm = T), 
            both = sum(frv.score > 0.511 & faa.pred),
            mean.frv = round(mean(frv.score), 2), mean.faa = round(mean(faa.score, na.rm = T), 2))

dt <- DT::datatable(df, options = list(dom = 't', pageLength = 20, ordering = FALSE), 
                caption = "Summary of Adhesin Prediction Results")
# the following code is from https://www.r-bloggers.com/2020/05/mimic-excels-conditional-formatting-in-r-2/
brks <- stats::quantile(x <- df[[2]], probs = seq(.05, .95, .05), na.rm = TRUE)
#clrs <- colorRampPalette(c("#fcdbdb", "#ff1515"))(length(brks)+1)
clrs <- colorRampPalette(brewer.pal(n = 4, name = "Reds"))(length(brks)+1)
for (j in 2:5){
  # Create breaks for shading column values high to low
  # Create shades of green for backgrounds
  # Format cells in j-th column
  dt <- DT::formatStyle(dt, j, backgroundColor = DT::styleInterval(brks, clrs))
}
# see https://yulab-smu.top/treedata-book/chapter7.html#attach-operator
# the attach operator "%<+%" works on ggtree objects and requires the first column
# of the data frame to contain the tip labels

GPI-anchor prediction

GPI-anchored proteins are characterized by an N-terminal signal peptide, which would direct the protein to the secretary pathway, and a C-terminal GPI-anchor peptide, which would be cleaved and replaced by the GPI-anchor, allowing the protein to be tethered to the cell wall.

For signal peptide, I used SignalP server. Its latest version is 5.0. But I also ran the sequences through their 4.1 version, with two settings. The results of the latter two are almost identical, except for one sequence “XP_024711350.1”, which is only included in the sensitive version, and has a probability lower than 0.5.

# Signal peptide
gff.names <- c("id", "source", "name", "start", "end", "prob", "na1", "na2", "na3")
signalp5 <- read_tsv("output/web-download/signalp_5.0.gff", comment = "#", col_names = gff.names, col_types = "ccciidccc")

if("signalp" %in% names(seqInfo))
  seqInfo <- select(seqInfo, -signalp)

seqInfo <- left_join(seqInfo, select(signalp5, name = id, prob), by = c("name" = "name")) %>% 
  mutate(signalp = !is.na(prob)) %>% select(-prob)

For GPI-anchor prediction, I used the PredGPI server.

tmp <- read_delim("output/web-download/predgpi_result.txt", delim = "|", col_names = c("name","fp","omega"))
Rows: 104 Columns: 3
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "|"
chr (3): name, fp, omega

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
pred.gpi <- tmp %>% 
  mutate(name = str_sub(name,2,-2), # remove > and the trailing space
         fp = as.numeric(str_sub(fp, 9, -2)), # extract the numeric part
         is.gpi = fp <= 0.01,    # based on the cutoff of the PredGPI server (prob < 99% -> not GPI-anchored)
         omega = str_sub(omega, 8),
         cleaveRes = str_sub(omega, 1, 1),
         cleavePos = as.integer(str_sub(omega, 3))
         ) %>% 
  left_join(select(seqInfo, name, length), by = c("name" = "name"))

# remove the column if it already exists
if("pred.gpi" %in% names(seqInfo))
  seqInfo <- select(seqInfo, -pred.gpi)
seqInfo <- left_join(seqInfo, select(pred.gpi, name, pred.gpi = is.gpi), by = c("name"="name"))
df1 <- seqInfo %>% 
  group_by(species_id) %>% 
  summarize(SignalP = sum(signalp), GPI = sum(pred.gpi), All = sum(frv.score > 0.511 & signalp & pred.gpi)) %>% 
  right_join(df) %>% 
  select(species = species_id, Total = n, FRV = fungalRV, SP = SignalP, GPI, All) %>% 
  pivot_longer(Total:All, names_to = "type", values_to = "number") %>% 
  mutate(type = factor(type, levels = unique(type)))
Joining, by = "species_id"
p <- ggplot(df1, aes(x = type, y = species)) + 
  scale_y_discrete(limits = rev) +
  geom_tile(aes(fill = number), color = "white", alpha = 0.4) +
  geom_text(aes(label = number), color = "black", size = 4) +
  scale_fill_distiller(palette = "Greys",direction = 1) +
  scale_x_discrete(position = "top") +
  theme_cowplot() + theme(axis.title = element_blank(), legend.position = "none")
p
ggsave("output/img/20211024-homologs-prediction-summary.png", width = 4.5, height = 5)

Summary

  • Most of the Hil homologs in Ascomycetes passed both the GPI-anchor predictions and also the FungalRV predictor, suggesting that they are likely to be cell wall proteins and may play an adhesin function (FaaPred predicted fewer adhesins, but the overall pattern doesn’t change).

Tandem repeat structures

The non-NTD portion of the proteins evolve rapidly and many of them contain tandem repeats. Therefore, characterizing and visualizing the type, number and spatial distribution of the tandem repeats serve to highlight the differences in the non-NTD part of the proteins in this family.

To identify and group tandem repeats, I used XSTREAM with the following parameters java -Xmx1000m -Xms1000m -jar ~/sw/XSTREAM/xstream.jar $in -i.7 -I.7 -g3 -e2 -L15 -z -G -O -Asub.txt. The parameters were chosen to identify degenerate tandem repeats that occur at least two times and must be a minimum length of 5 a.a. or longer and the minimum length of a tandem repeat domain (=period x copy #) must be greater than 15 a.a. Please see script/xstream.sh for explanation of the parameters.

tandem <- read_tsv("output/xstream/XSTREAM_XP_028889033_homologs_sub_i0.7_g3_m5_L15_chart.tsv",
                   col_types = "ciiifidcccd", comment = "#")
# now let's create a tibble for plotting, which would contain each instance of the tandem repeat on a separate row
tandem.div <- tandem %>% 
  rowwise(name) %>% 
  summarize(div = list(c(seq(from = start, to = end, by = period), end)), .groups = "drop") %>% 
  unnest(div)

# summarize stats of tandem repeats
repeats <- tandem %>% 
  group_by(type, period) %>% 
  summarize(n = n(), copyMean = mean(copyN), .groups = "drop") %>% 
  mutate(length = period * copyMean)

Calculate length of tandem repeats as a percentage of the protein length minus the PF11765 domain

# tandem repeat length distribution
ggplot(repeats, aes(length)) + geom_histogram(bins = 40) + 
  scale_x_continuous(breaks = c(10,20,40,80,160,320,640,1280), trans = "log2") +
  theme_cowplot()

# tandem repeat length cumulative distribution
ggplot(repeats, aes(length)) + stat_ecdf(geom = "step") + 
  scale_x_continuous(breaks = 10*2^seq_len(9), trans = "log2") +
  ylab("Probability") + xlab("length (= period * mean copies)") + 
  theme_cowplot() + panel_border(color = "black") #+ background_grid()

Feature map for homologs

The goal is to produce a cartoon-like plot for each homolog outlining their main features, such as the locations of the PFam domains (mainly the Hyp_reg_CWP), locations of the signal peptide and GPI-anchor, distribution of TANGO sequences. Note that all these features can be represented as a range with associated metadata. So the first step is to collect the coordinates of the features

Load Pfam domains

# Pfam domains
pfam <- read_tsv("output/web-download/HMMER-HMMScan-Pfam-hits.tsv", col_types = "ciiiicciiidddiic")
# save feature file for Jalview examination
# pfam %>% filter(grepl("XP_028889033",seq_id)) %>% select(hmm_name, seq_id, envelope_start, envelope_end) %>% mutate(featuretype = "domain") %>% write_tsv("XP_028889033_features.jalview")
# I manually edited the feature file, so I commented out the line above to avoid accidentally 
# overwriting my own edits

Organize and combine the tandem repeats features

#tr.th <- 100 # arbitrary threshold for distinguishing "short" from "long" TRs
tr <- tandem %>% 
  left_join(select(repeats, type, copyMean), by = c("type" = "type")) %>% 
  mutate(type = "Tandem Repeats") %>% 
  select(name, type, start, end)
# GPI-anchor
# use pred.gpi
# feature set
# structure: id  feature  start  end
seqLen <- seqInfo %>% mutate(start = 1) %>% select(name, start, end = length)
feature <- bind_rows(
  pfam %>% select(name = seq_id, type = hmm_name, start = envelope_start, end = envelope_end) %>% 
    filter(type == "Hyphal_reg_CWP"),
  # extend the signal peptide segment by 10 amino acids to make it more visible
  signalp5 %>% mutate(type = "Signal Peptide", end = end + 10) %>% select(name = id, type, start = start, end),
  # extend the GPI-anchor C-terminus segment by 20 amino acids to make it more visible
  pred.gpi %>% filter(is.gpi) %>% mutate(type = "GPI-anchor", start = cleavePos-10) %>% 
    select(name, type, start, end = length),
  tr
) 

# in order to plot properties of the sequences in an order that is consistent with the sequences' position in the gene tree
genetreeOrder <- scan("data/reorder_by_gene_tree.txt", what = "character")
Read 104 items
seqLen$name <- ordered(seqLen$name, levels = rev(genetreeOrder))

feature <- feature %>% 
  mutate(name = ordered(name, levels = rev(genetreeOrder)),
         type = ordered(type, levels = c("entire protein", "Hyphal_reg_CWP", "Signal Peptide", "GPI-anchor", "Tandem Repeats")))
feature.colors <- c(Hyphal_reg_CWP = "#3d85c6", "Signal Peptide" = "#cc0000", "GPI-anchor" = "#6a3d9a", "Tandem Repeats" = "#af8400bb")

Plot domain organization

# plot
p0 <- ggplot(seqLen, aes(x = name, y = start, xend = name, yend = end)) + 
  #geom_segment(color = "black", size = 2.1) +
  geom_segment(color = "gray80", size = 2)
p1 <- geom_segment(data = feature, aes(color = type), size = 2)
p2 <- geom_segment(data = tandem.div, aes(x = name, xend = name, y = div, yend = div + 1.5), size = 2, color = "white")
p3 <- p0 + p1 + coord_flip() + theme_classic() + 
  scale_color_manual(values = feature.colors) +
  scale_y_continuous(expand = expansion(mult = c(0.02, 0.02))) +
  theme(axis.text.y = element_text(size = 4), axis.title.y = element_blank(),
        axis.line.y = element_blank(), axis.ticks.y = element_blank(),
        axis.line.x = element_blank(),# axis.ticks.x = element_blank(),
        legend.position = c(0.75, 0.35), 
        legend.text = element_text(size = 12), legend.title = element_text(size = 14),
        plot.title = element_text(hjust = 0.5), 
        panel.background = element_rect(fill = alpha("lightblue",0.1))) +
  labs(y = "Position in sequence", x = "Sequences", color = "FEATURES")
#plot_grid(p.gtree, p3, ncol = 2, align = 'v', rel_widths = c(1,2), scale = c(1.01,1))
ggsave("output/img/20210625-domain-tandem-repeats.png", width = 6, height = 7.5)

Fig. ? Blue boxes indicate the PF11765 domains while all other non-grey boxes indicate XSTREAM-determined tandem repeat domains. Colors are used to group highly similar tandem repeats. The black thin lines demarcate adjacent tandem repeat units. The table below shows the copy number, period and consensus sequence for each tandem domain organized by the host sequences.

Separate TR types

DT::datatable(
  tandem %>% 
    dplyr::rename(seqL = seqLength, err = consensus_error, seq = consensus_nogap) %>% 
    select(-seqAlign, -type, -consensus_gap, -seq, seq) %>% 
    arrange(desc(name)),
  fillContainer = FALSE, options = list(pageLength = 10)
)

Repeat the above analysis but distinguishing between all different TR types

tr1 <- tandem %>% 
  left_join(select(repeats, type, copyMean), by = c("type" = "type")) %>% 
  mutate(type = paste0("TR-", type),
         tip = paste0(consensus_nogap,
                      "\ntype: ", type,
                      "\nperiod: ", period, 
                      "\ncopyN: ", copyMean),
         name = ordered(name, levels = rev(genetreeOrder))) %>% 
  select(name, type, start, end, tip)
require(RColorBrewer)
tr.th <- 100 # arbitrary threshold for distinguishing "short" from "long" TRs
tr.col <- character(nrow(repeats)) # create a color vector
short.rp <- which(repeats$length < tr.th) # identify the short repeats indices
long.rp <- setdiff(1:nrow(repeats), short.rp) # the long repeats indices
set.seed(123) # for reproducibly shuffling the order before assigning the colors
short.rp <- sample(short.rp) # shuffle the indices for short tandem repeats
tr.col[short.rp] <- colorRampPalette(brewer.pal(12, "Paired")[seq(3,11,by=2)])(length(short.rp)) # assign the short repeats a lower contrast color
set.seed(231) # for reproducibly shuffling the order before assigning the colors
long.rp <- sample(long.rp)
tr.col[long.rp] <- colorRampPalette(brewer.pal(12, "Paired")[seq(4,12,by=2)])(length(long.rp)) # assign the long repeats a higher contrast color
# -- desaturate the colors -- 
# https://stackoverflow.com/questions/26314701/r-reducing-colour-saturation-of-a-colour-palette
library(colorspace)   ## hsv colorspace manipulations

## Function for desaturating colors by specified proportion
desat <- function(cols, sat=0.5) {
    X <- diag(c(1, sat, 1)) %*% rgb2hsv(col2rgb(cols))
    hsv(X[1,], X[2,], X[3,])
}

tr.col <- desat(tr.col, sat = 0.8)
# -- finish --
tr.col <- paste0(tr.col, "CC") # add 20% transparency to the TR features
names(tr.col) <- paste0("TR-", repeats$type) # name the colors by the TR types
repeats$color <- tr.col

Combine domains, SP and GPI-anchor with TR features.

# combine sequence features with tandem repeats
feature1 <- bind_rows(filter(feature, type != "Tandem Repeats"), tr1) %>% 
  mutate(type = ordered(type, levels = c("Hyphal_reg_CWP", "SignalP", "GPI-anchor", unique(tr1$type))))
feature.col1 <- c(feature.colors[1:3], tr.col) # remove the singel "Tandem Repeats" type
write_tsv(feature1, file = "output/misc/R-feature-table.tsv", col_names = TRUE)
# plot
p0 <- ggplot(seqLen, aes(x = name, y = start, xend = name, yend = end)) + 
  #geom_segment(color = "black", size = 2.1) +
  geom_segment(color = "gray80", size = 2)
p1 <- geom_segment(data = feature1, aes(color = type, text = tip), size = 2)
p2 <- geom_segment(data = tandem.div, aes(x = name, xend = name, y = div, yend = div + 1.5), size = 2, color = "white")
p3 <- p0 + p1 + coord_flip() + theme_classic() + 
  scale_color_manual(values = feature.col1) +
  scale_y_continuous(expand = expansion(mult = c(0.02, 0.02))) +
  theme(axis.text.y = element_text(size = 4), axis.title.y = element_blank(),
        axis.line.y = element_blank(), axis.ticks.y = element_blank(),
        axis.line.x = element_blank(),# axis.ticks.x = element_blank(),
        legend.position = "none", plot.title = element_text(hjust = 0.5),
        panel.background = element_rect(fill = alpha("lightblue",0.1))) +
  labs(y = "Position in sequence", x = "Sequences", color = "FEATURES")
# plot_grid(p.gtree, p3 + p2, ncol = 2, align = 'v', rel_widths = c(1,2), scale = c(1.01,1))
ggsave("output/img/20210626-domain-tandem-repeats-distinct-color.png", width = 5, height = 7.5)
# plot
#require(plotly)
ggplotly(p3, tooltip = "text", width = 900, height = 900)

Similarity and divergence

Length distribution

# plot the length distribution of the homologs in each species
p <- ggplot(seqInfo, aes(x = species_id, y = length)) + 
  geom_boxplot(outlier.shape = NA) + geom_hline(yintercept = 500, linetype = 2) +
  #stat_summary(fun.data = "mean_cl_boot", color = "red") +
  geom_dotplot(binwidth = 100, binaxis = "y", stackdir = "center", dotsize = 0.5, 
               binpositions = "all", fill = rgb(0,0,0,0.5), color = rgb(0,0,0,0.6)) + 
  scale_x_discrete(limits = rev) +
  coord_flip() +  labs(title = "Distribution of XP_028889033 homologs' length") +
  theme(axis.title.y = element_blank(), axis.text.y = element_blank())
  
plot_grid(p.tree, p, align = "h", ncol = 2, rel_widths = c(1,1.5), scale = c(1.08,1))

Fig. 5 Distribution of Hil family homologs’ length across species. The shadings in the species tree on the left is the same as in Figure 1. Protein lengths are plotted both as a dotplot (binned in 100 a.a. bins) and a boxplot, where the box represents the interquartile range, the middle bar indicates the median and the whisker 1.5 times the interquartile range.

Discussion

  • The one sequence below 500 a.a. is from N. delphensis, which is labeled as a partial CDS.
  • Majority of the proteins in the list are 500-2000 a.a., with a few exceptionally long
  • Not only do Saccharomycetaceae species have fewer Hil family homologs, the ones they have are also short (< 1000 a.a.) with the exception of C. glabrata

Protein length vs tandem repeat content

Examine the relationship between the entire protein length and tandem repeat content

# calculate the length of the PF11765 domain in each protein
pf11765.length <- feature %>% 
  filter(type == "Hyphal_reg_CWP") %>% 
  mutate(domainLen = end - start + 1) %>% 
  # need the following step because one protein has two instances of PF11765
  group_by(name) %>% 
  summarize(dmLen = sum(domainLen))

# calculate repeat content
tr.length <- tandem %>% 
  mutate(trLength = end - start + 1) %>% 
  group_by(name, seqLength) %>% 
  summarize(trLen = sum(trLength)) %>% 
  dplyr::rename(length = seqLength) %>% 
  right_join(select(seqInfo, name, length, species_id, species_gr),
             by = c("name","length")) %>% 
  mutate(trLen = ifelse(is.na(trLen), 0, trLen)) %>% 
  left_join(pf11765.length, by = "name") %>% 
  mutate(nonNTDLen = length - dmLen, tr.content = trLen/nonNTDLen)
`summarise()` has grouped output by 'name'. You can override using the `.groups` argument.
tr.length %>% 
  ggplot(aes(x = trLen, y = length)) + 
  geom_point(size = 2.5, alpha = 0.7) + 
  #geom_smooth(method = "lm") +
  #scale_color_manual(name = "Clade", values = sps.color) +
  ylab("Protein length") + xlab("Tandem repeat length") + ylim(0, 4500) +
  theme_cowplot() + panel_border(color = "black")
ggsave("output/img/20210627-protein-length-evol-driven-by-tandem-repeats.png", width = 6, height = 4.5)

Shorter proteins seem to have a lower content of tandem repeats as a percentrage of their non NTD portion of the protein.

tr.length %>% 
  mutate(group = cut(length, breaks = c(0,1000,1500,5000), labels = c("<1000","<1500",">1500"))) %>% 
  ggplot(aes(x = group, y = tr.content)) + 
  geom_boxplot(outlier.shape = NA) + geom_jitter(width = 0.2, size = 2, alpha = 0.8) +
  #geom_hline(yintercept = 0.5, linetype = 2, color = "gray30") +
  #geom_vline(xintercept = 1500, linetype = 2, color = "gray30") +
  #scale_color_manual(name = "Clade", values = sps.color) +
  ylab("Tandem repeat %") + xlab("Protein length") + #coord_flip() +
  theme_cowplot(font_size = 16) + panel_border(color = "black")
ggsave("output/img/20211120-tandem-repeat-proportion.png", width = 5, height = 3)

# https://stackoverflow.com/questions/7549694/add-regression-line-equation-and-r2-on-graph
# # GET EQUATION AND R-SQUARED AS STRING
# # SOURCE: https://groups.google.com/forum/#!topic/ggplot2/1TgH-kG5XMA
# modified by Bin He 2021-06-28
lm_eq <- function(m){
    eq <- substitute(italic(y) == a + b %.% italic(x)*","~~italic(r)^2~"="~r2, 
         list(a = format(unname(coef(m)[1]), digits = 4),
              b = format(unname(coef(m)[2]), digits = 4),
             r2 = format(summary(m)$r.squared, digits = 3)))
    as.character(as.expression(eq));
}

lm.obj <- lm(nonNTDLen ~ trLen, tr.length)

tr.length %>% 
  ggplot(aes(x = trLen, y = nonNTDLen)) + 
  geom_smooth(method = "glm", formula = y~x, se = FALSE) +
  geom_point(size = 2.5, alpha = 0.7) + 
  annotate(geom = "text", x = 2000, y = 500, label = lm_eq(lm.obj), parse = TRUE, size = 5) +
  xlab("Tandem repeat length") + ylab("Length of non-NTD part") +
  theme_cowplot() + panel_border(color = "black")
ggsave("output/img/20210628-protein-nonNTD-length-driven-by-tandem-repeat.png", width = 5, height = 4.5)

Discussion

  • Tandem repeat content is highly correlated to the non-NTD portion of the protein length, with an estimated slope of close to 1, suggesting that protein length evolution is strongly driven by the acquirement, expansion or contraction of tandem repeats.
  • Homologs shorter than 1300 amino acids in the non-NTD portion have more variability in the tandem repeat proportion, with some having very little tandem repeats.

Serine/Threonine content

S/T sites are potential sites for O-glycosylation, which could increase the rididity of the stalk of the protein and allow the N-terminal domain to protrude out of the cell wall facing the exterior. More evidence for the importance of O-glycosylation in a serine/threonine-rich domain can be found here.

In whole protein

The goal here is to compare the S/T frequencies in the Hil proteins to the proteome average. First we need to calculate the S/T frequencies in the Hil proteins, either in the whole protein or excluding the PF11765 domain. This is done with a script in the script folder named Hil-ST-freq.sh

To help with the latter task (in the non-PF11765 domain part), I export the PF11765 domain boundaries as a BED format file

feature %>% 
  filter(type == "Hyphal_reg_CWP") %>% 
  select(name, start, end) %>% 
  write_tsv(file = "data/XP_028889033_homologs_PF11765.BED", col_names = FALSE)

Read in the S/T frequencies

ST.full <- read_tsv("output/ST-freq/20211121-Hil-full-STfreq.out", col_types = "ciii")
ST.noNTD <- read_tsv("output/ST-freq/20211121-Hil-noPF11765-STfreq.out", col_types = "ciii")

To determine the background frequency of Serine and Threonine in the proteome(s), I modified the calc_aafreq_gz.py script I wrote a long time ago for calculating the cystein and dibasic residues. I then copied four proteome fasta files, for C. albicans, C. glabrata, S. cerevisiae and C. auris and applied the script on them (using a wrapper script called S-T-freq.sh). Below I will look at both the proteome average and the distribution of S/T in individual proteins across the proteome.

tmp.files <- list.files(path = "output/ST-freq/", pattern = "*ST-freq.tsv.gz")
files <- file.path("output/ST-freq", tmp.files)
names(files) <- gsub("-", " ", tmp.files) %>% word(1, 1)
bgST.freq <- files %>% 
  map(~read_tsv(., col_types = cols())) %>% 
  bind_rows(.id = "Species") %>% 
  mutate(freqS = Ser/length, freqT = Thr/length,
         Ser = NULL, Thr = NULL,
         Species = paste0(str_sub(Species, 1, 1), ". ", str_sub(Species, 2))) %>% 
  pivot_longer(cols = starts_with("freq"), names_to = "Residue", names_prefix = "freq", values_to = "frequency")

Is there any correlations between protein length and S/T frequency?

bgST.freq %>% 
  ggplot(aes(x = length, y = frequency)) +
  geom_hex() + facet_grid(Residue ~ Species, scales = "free_y") + 
  scale_x_log10() + xlab("Protein length")

Doesn’t seem to be significant.

Now let’s combine the Hil protein S/T frequences as an extra “species” into the bgST.freq

tmp <- ST.full %>% 
  mutate(Species = "Hil_full", S = Ser/length, `T` = Thr/length) %>% 
  pivot_longer(cols = c(S, `T`), names_to = "Residue", values_to = "frequency") %>% 
  select(Species, ID, length, Residue, frequency) %>% 
  bind_rows(filter(bgST.freq, Species != "S. cerevisiae"))
#tmp <- ST.noNTD %>% 
#  mutate(Species = "Hil-PF11765", S = Ser/length, `T` = Thr/length) %>% 
#  pivot_longer(cols = c(S, `T`), names_to = "Residue", values_to = "frequency") %>% 
#  select(Species, ID, length, Residue, frequency) %>%
#  bind_rows(tmp)

p <- tmp %>% 
  ggplot(aes(x = Species, y = frequency, fill = Residue)) + 
  geom_boxplot(position = position_dodge(0.9), outlier.size = 0.2, outlier.alpha = 0.5) + 
  #stat_summary(
  #  fun.data = "mean_sdl",  fun.args = list(mult = 1), 
  #  geom = "pointrange", color = "black", position = position_dodge(0.9)
  #) +
  scale_fill_manual(values = c("T" = "skyblue3", "S" = "lightblue1")) +
  #scale_fill_brewer() +
  theme_cowplot() + panel_border(color = "black") +
  theme(axis.title.x = element_blank())

p# + p1 + scale_color_manual(values = c("S" = alpha("gray20", 0.5), "T" = alpha("gray20", 0.5)))
ggsave("output/img/20211121-Hil-ST-freq-compared-to-proteome.png", width = 5, height = 2.5)

bgST.freq %>% 
  group_by(Species, Residue) %>% 
  summarize(mean = round(mean(frequency),3)) %>% 
  pivot_wider(names_from = Residue, values_from = mean)
`summarise()` has grouped output by 'Species'. You can override using the `.groups` argument.

Sliding window estimates

To determine the S/T frequency in the XP_028889033 homologs, I ran the program freak from the EMBOSS suite with the parameters of 100 aa sliding window and a step size of 10 aa. After reformating the output, the rest of the analysis is accomplished below.

# load data
ST.freq <- read_tsv("output/ST-freq/ST_freq_100_10_freak.out.gz", col_types = "cid")
S.freq <- read_tsv("output/ST-freq/S_freq_100_10_freak.out.gz", col_types = "cid")
T.freq <- read_tsv("output/ST-freq/T_freq_100_10_freak.out.gz", col_types = "cid")
# convert sequence name column to an ordered list sorted based on the gene tree sequence
ST.freq <- ST.freq %>%  mutate(id = ordered(id, levels = rev(genetreeOrder))) # this will produce the desired order
S.freq <- S.freq %>%  mutate(id = ordered(id, levels = rev(genetreeOrder))) # this will produce the desired order
T.freq <- T.freq %>%  mutate(id = ordered(id, levels = rev(genetreeOrder))) # this will produce the desired order
ggplot(ST.freq, aes(x = id, y = pos)) +  geom_tile(aes(fill = freq)) +
  coord_flip() + theme_classic() + scale_fill_distiller(palette = "RdGy", limits = c(0, 0.8), oob = scales::squish, breaks = seq(0, 0.6, by = 0.2)) +
  theme(axis.text.y = element_text(size = 5),
        axis.line.y = element_blank(), axis.ticks.y = element_blank(),
        axis.line.x = element_blank(),# axis.ticks.x = element_blank(),
        legend.position = c(0.85,0.35),
        panel.background = element_rect(fill = alpha("lightblue",0.5))) +
  ylim(1, 4500) + labs(y = "Position in sequence", x = "Sequences", color = "Frequency") + 
  ggtitle("Serine/Threonine frequency in 100 aa sliding windows")

ggsave("output/img/20201223-homologs-ST-freq-100aa-window.png", bg = "transparent", width = 7, height = 7)
ST.comb <- bind_rows(Ser = S.freq, Thr = T.freq, .id = "Var") %>% 
  mutate(Var = ordered(Var, levels = c("Ser","Thr")))
p.st <- ggplot(ST.comb, aes(x = id, y = pos)) + geom_tile(aes(fill = freq), size = 2) + 
  facet_wrap(~Var, scales = "fixed") + theme_classic() +
  coord_flip() + 
  #scale_fill_distiller(palette = "RdGy", limits = c(-0.1, 0.75), oob = scales::squish, breaks = seq(0,0.7,by=0.1)) +
  scale_fill_gradient2(low = "gray10", high = "#006000", mid = "gray90", midpoint = 0.05, breaks = seq(0,0.7,by=0.1)) +
  #scale_fill_steps2(low = "#000000", high = "#b30000", midpoint = 0.1, breaks = seq(0,0.7,by=0.1)) +
  #scale_fill_viridis_b(n.breaks = 10, begin = 0.1) +
  theme(axis.text.y = element_text(size = 4), axis.title.y = element_blank(),
        axis.line.y = element_blank(), axis.ticks.y = element_blank(),
        axis.line.x = element_blank(),# axis.ticks.x = element_blank(),
        strip.background = element_blank(),
        legend.position = c(0.92,0.40),
        panel.background = element_rect(fill = alpha("lightblue",0.5))) +
  ylim(1, 4500) + ylab("Position in sequence") + xlab("Sequences") 
#plot_grid(p.gtree, p.st, ncol = 2, align = 'v', rel_widths = c(1,3), scale = c(0.99,1))
p.st

ggsave("output/img/20201223-ST-freq-composite.png", width = 7, height = 7.5)

TANGO prediction of β-aggregation prone sequences

The amyloid-like \(\beta\)-aggregation prone sequences have the ability to mediate self-aggregation, which boosts the local concentration of the adhesin molecules on the cell-surface. Similar to the S/T frequency above, we would like to use the output from the prediction algorithm, TANGO, to visulize the distribution of such sequence motifs along the length of the XP_028889033 homolog sequences.

TANGO predictions and the subsequent extraction of \(\beta\)-aggregation prone sequences were documented in separate Python script, README files and the tango.Rmd.

# here we only look at the extracted tango sequences
tango <- read_tsv("data/tango_summary_table.tsv.gz", col_types = "cciiidddicc")
# reorder the sequences for plotting
tango$name <- ordered(tango$name, levels = rev(genetreeOrder))
# plot
p1 <- ggplot(seqLen, aes(x = name, y = start, xend = name, yend = end)) + geom_segment(color = "gray80", size = 2)
p2 <- geom_segment(data = filter(feature, type == "Hyphal_reg_CWP"), aes(x = name, y = start, xend = name, yend = end), size = 2, color = "#3d84c6")
p3 <- geom_segment(data = tango, aes(x = name, xend = name,  y = ifelse(start-4 >= 0, start-4, 0), yend = end + 4, color = median), size = 2)
p4 <- p1 + p2 + p3 + coord_flip() + theme_classic() + 
  scale_color_viridis_c(limits = c(5,101), breaks = c(5,seq(25,100,25)), direction = -1) +
  theme(axis.text.y = element_text(size = 4), axis.title.y = element_blank(),
        axis.line.y = element_blank(), axis.ticks.y = element_blank(),
        axis.line.x = element_blank(), axis.ticks.x = element_blank(),
        plot.title = element_text(hjust = 0.5),
        legend.position = c(0.75,0.35),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 12),
        panel.background = element_rect(fill = alpha("lightblue",0.1))) +
  ylim(-2, 4500) + labs(y = "Position in sequence", x = "Sequences", color = "TANGO score") + 
  ggtitle("TANGO hits with Hyphal_reg_CWP domain masked")
p4
#plot_grid(p.gtree, p4, ncol = 2, align = 'v', rel_widths = c(1,2.5), scale = c(1.01,1))
ggsave("output/img/20210110-tango-score-segment.png", width = 6, height = 7.5)

Number and spacing of TANGO hits in each protein sequence

Some of the following analysis is modeled after another R markdown 20201031-tango.Rmd.

How many TANGO predicted β-aggregation sequences does each homolog has, either in the entire protein or in the non-NTD portion? To do so I’ll take advantage of the GRanges package to distinguish TANGO hits that are within or outside the PF11765 domain(s) for each protein. First, I need to convert the feature table into a GRanges object with the sequence names used as chromosome names and start and end as the IRanges start and end. Then I’ll convert the tango output also into a GRanges object in the same way, with the exception of keeping the median as the score field.

pf11765.gr <- feature %>% 
  filter(type == "Hyphal_reg_CWP") %>% 
  select(seqname = name, start, end) %>% 
  makeGRangesFromDataFrame(ignore.strand = TRUE)

tango.gr <- tango %>% 
  select(seqname = name, start, end, score = median) %>% 
  makeGRangesFromDataFrame(keep.extra.columns = TRUE, ignore.strand = TRUE)

# count the number of tango hits in the pf11765 domain
tango.gr.in <- subsetByOverlaps(tango.gr, pf11765.gr) %>% 
  as_tibble() %>% 
  mutate(name = ordered(seqnames, levels = rev(genetreeOrder)), InNTD = TRUE) %>% 
  select(name, start, end, InNTD)

tango1 <- tango %>% 
  mutate(InNTD = FALSE) %>% 
  rows_update(tango.gr.in, by = c("name", "start", "end"))

Once I have the new tibble tango1 that contains the InNTD column, I can calculate summary statistics:

tango1.sum <- tango1 %>% 
  group_by(name) %>% 
  summarize(n.all = sum(!InNTD), n.gt30 = sum(round(median,0) >= 30 & !InNTD)) %>% 
  left_join(select(seqInfo, name, length), by = "name")

cat("Count the total number of tango hits outside the NTD: ")
Count the total number of tango hits outside the NTD: 
table(cut(tango1.sum$n.all, breaks = c(0,1,5,10,Inf), right = FALSE))

   [0,1)    [1,5)   [5,10) [10,Inf) 
       6       41       21       36 
cat("Among the above tango hits, only count those with median score greater than 30: ")
Among the above tango hits, only count those with median score greater than 30: 
table(cut(tango1.sum$n.gt30, breaks = c(0,1,5,10,Inf), right = FALSE))

   [0,1)    [1,5)   [5,10) [10,Inf) 
      14       60       12       18 

C. auris Hil1-4 and their MDR orthologs are exceptional in TANGO hits number and distribution. They are on average longer than the rest of the homologs

# the subset of homologs are 22-43 in the genetreeOrder vector. use this to do the following
tango1.sum %>%
  group_by(`Hil1-4` = name %in% genetreeOrder[18:43]) %>% 
  summarize(n = n(), mLen = median(length), nTANGO = median(n.all), nTANGO30 = median(n.gt30))

How about the spacing between the replicates?

# first count the number of out-of-NTD, median score >= 30 hits per sequence
motif.per.seq <- tango1 %>% 
  group_by(name) %>% 
  summarize(n.all = sum(!InNTD & round(median, 0) >= 30))

# next we will filter the tango dataset in order to recalculate the intervals
# this will result in 14 sequences to be dropped since they have 0 hits meeting
# the criteria. we will add them back by right_join() with the tibble above
motif.per.seq <- tango1 %>% 
  # limit to sequences not in the PF11765 domain and with median score >= 30
  filter(!InNTD, round(median,0) >= 30) %>% 
  group_by(name) %>% 
  # recalculate the interval since we are now limiting the hits to >= 30
  mutate(interval = start - lag(end) - 1) %>% 
  # summarize the results at a sequence level
  summarize(n.type = length(unique(seq)),
            n.all = n(),
            medScore = round(mean(median),1),
            IVT = round(mean(ivt),2),
            avg.intv = round(mean(interval, na.rm = T),1), 
            IQR.intv = round(IQR(interval, na.rm = T)/1.349 ,1),
            # median absolute deviation is a robust measure of the scale parameter
            # https://www.rdocumentation.org/packages/stats/versions/3.6.2/topics/mad
            mad.intv = round(mad(interval, na.rm = T),1),
            seqs = paste(unique(seq), collapse = ","),
            .groups = "drop_last") %>% 
  right_join(motif.per.seq, by = c("name", "n.all")) %>% 
  arrange(desc(n.all), desc(mad.intv))
DT::datatable(motif.per.seq)

# add extra information for plotting below
motif.per.seq <- motif.per.seq %>% 
  left_join(select(seqInfo, species_id, species_gr, name), by = "name") %>% 
  mutate(reg.spaced = ifelse(n.all > 3 & mad.intv < 5, TRUE, FALSE),
         mad.intv = ifelse(n.all > 2, mad.intv, NA),
         species = ordered(species_id, levels = levels(species_id), labels = 
                             paste0(str_sub(levels(species_id),1,1), ". ", 
                                    str_sub(levels(species_id), 2))
         ),
         species = reorder(species, desc(species)),
         species_id = NULL)

Export the tango motif per seq result

motif.per.seq1 %>% 
  mutate(id = gsub("_[a-zA-Z]+$", "", name),
         species = gsub(".*_([A-Z])([a-z]+)$", "\\1\\. \\2", name)) %>% 
  select(id, species, species_gr, n.type, n.all, avg.intv, mad.intv, seqs) %>% 
  write_tsv("output/tango/20210904-tango-summary-table.tsv")
#  mutate(Clade2 = ifelse(species %in% clavispora, "Clavispora",
#                         ifelse(species %in% candida, "Candida", 
#                                ifelse(species %in% saccharo, "Saccharomycetaceae", "other"))))
#p0 <- motif.per.seq %>% 
#  ggplot(aes(x = species)) + geom_bar() + coord_flip() +
#  # thanks to https://stackoverflow.com/questions/10834382/ggplot2-keep-unused-levels-barplot
#  scale_x_discrete(drop = FALSE) +
#  scale_y_continuous(breaks = c(0,5,10,15), minor_breaks = NULL) + 
#  ylab("# homologs")

col.regspc <- c("TRUE" = "#af8400", "FALSE" = alpha("grey30", 0.5))
p1 <- motif.per.seq %>% 
  ggplot(aes(x = species, y = n.all)) + 
  geom_jitter(aes(color = reg.spaced), size = 2.2, width = 0.3, height = 0) +
  #geom_dotplot(aes(fill = reg.spaced), binaxis = "y", binwidth = 4, 
  #             stackdir = "center", width = 0.5, binpositions = "all") +
  #geom_text(aes(label = ifelse(n.all > 2 & mad.intv < 5, "*", "")),
  #          color = "grey20") +
  #scale_size_manual(values = c(1,2)) + guides(size = "none") +
  #scale_shape_manual(name = "Regularly spaced", values = c(21,19)) +
  scale_color_manual(name = "Regularly spaced", values = col.regspc) +
  coord_flip() + ylab("# TANGO hits/protein") +
  scale_y_continuous(trans = "pseudo_log", breaks = c(0:5,10,20,40),
                     minor_breaks = NULL)
legend <- get_legend(p1 + guides(color = guide_legend(nrow = 1), pch = guide_legend(nrow = 1)) +
                       theme(legend.position = "bottom"))
  
p2 <- motif.per.seq %>% 
  mutate(mad.intv = ifelse(is.na(mad.intv), 500, mad.intv)) %>% 
  ggplot(aes(x = species, y = mad.intv)) + 
  geom_jitter(aes(color = reg.spaced), size = 2.2, width = 0.3, height = 0) +
  #geom_dotplot(aes(fill = reg.spaced), binaxis = "y", binwidth = 0.5, 
  #             stackdir = "center", width = 0.3, binpositions = "all") +
  #scale_shape_manual(name = "Regularly spaced", values = c(21, 19)) +
  scale_color_manual(name = "Regularly spaced", values = col.regspc) +
  scale_y_continuous(trans = "pseudo_log", 
                     breaks = c(0,5,25,100,500), minor_breaks = NULL) +
  coord_flip() + ylab("Variation of spacing (MAD)")

p3 <- theme_cowplot() + panel_border(color = "black") + background_grid() +
                    theme(axis.text.y = element_blank(), axis.title.y = element_blank(),
                          legend.position = "none")
prow <- plot_grid(p1 + p3, p2 + p3, ncol = 2)

plot_grid(legend, prow, ncol = 1, rel_heights = c(.1,1))

ggsave("output/img/20210630-tango-hits-number-and-interval.png", width =5.5, height = 5)

Evolutionary history

Species tree

Below is the relationship between the species studied in this analysis.

sps.tree <- read.tree(file = "data/20200724-species-tree.nwk")
p.tree <- sps.tree %>% 
  as_tibble() %>% 
  mutate(label = paste0(str_sub(label, 1, 1), ". ", str_sub(label, 2))) %>% 
  left_join(spsData, by = "label") %>% 
  as.treedata() %>% 
  ggtree(ladderize = FALSE) + xlim(0,17) + scale_y_reverse() +
  geom_tiplab(aes(color = patho), size = 3.8, fontface = "italic", offset = 0.3) +
  scale_color_manual(values = c("black","red"), guide = "none") +
  #geom_hilight(node = 27, fill = "magenta", alpha = 0.15) + # Clavispora
  geom_hilight(node = 23, fill = "#7F00FF", alpha = 0.15)  + # MDR
  #geom_hilight(node = 33, fill = "gold", alpha = 0.25) + # Candida
  geom_hilight(node = 29, fill = "pink", alpha = 0.25)     + # albicans
  #geom_hilight(node = 37, fill = "grey50", alpha = 0.15)  + # Sacchromycetaceae
  geom_hilight(node = 33, fill = "steelblue", alpha = 0.15)  # glabrata
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
  #geom_cladelabel(node = 22,  label = "Clavispora", offset = 3.7, color = "magenta", 
  #                offset.text = 0.2, angle = 270, hjust = .5, extend = 0.3) + # Clavispora
p.tree + 
  geom_cladelabel(node = 23,  label = "MDR", offset = 4.5, color = "purple", fontface = 2,
                  offset.text = 0.2, angle = 270, hjust = .5, extend = 0.3) + # MDR
  #geom_cladelabel(node = 27,  label = "Candida", offset = 2.7, color = "salmon", 
  #                offset.text = 0.2, angle = 270, hjust = .5, extend = 0.3) + # Candida
  geom_cladelabel(node = 29,  label = "albicans", offset = 3.5, color = "hotpink2", fontface = 2,
                  offset.text = 0.2, angle = 270, hjust = .5, extend = 0.3) + # albicans
  geom_cladelabel(node = 33,  label = "glabrata", offset = 3.5, color = "steelblue", 
                  offset.text = 0.2, angle = 270, hjust = .5, extend = 0.3) # Sacchromycetaceae

Fig. 1 Phylogenetic relationship of the species analyzed in this study. This species tree is based on the Maximum likelihood phylogeny from Muñoz et al. 2018 (PMID: 30559369) for the most part, with the exception of the species in the Saccharomycetaceae family, which is based on Gabaldón et al. 2016 (PMID: 27493146).

Note that he placement of Debaryomyces hansenii differs between the above two publications. The former placed it closer to the Clavispora genus while the latter placed it closer to the Candida genus. We based ours on the first publication. Another large scale phylogenetic analysis (Shen et al. 2018 (PMID: 30415838)), also placed D. hansenii closer to the Candida genus. However, in both studies, which reported bootstrap support values in addition to the phylogeny, the support for the part of the tree involving D. hansenii is relatively low suggesting uncertainty in resolving the relationship. We chose to place D. hansenii closer to the Clavispora genus because this is more congruent with the gene tree for the Hil family proteins we inferred below.

Species tree for the side of a figure

#p.tree <- sps.tree %>% as_tibble() %>% 
#  mutate(label = paste0(str_sub(label, 1, 1), ". ", str_sub(label, 2))) %>% 
#  as.treedata() %>% ggtree(ladderize = FALSE) + xlim(0,15) + scale_y_reverse() +
#  geom_tiplab(size = 5, align = TRUE, linesize = .5, fontface = "italic", offset = 0.1) +
#  geom_hilight(node = 22, fill = "hotpink", alpha = 0.2) + # Clavispora
#  geom_hilight(node = 23, fill = "purple", alpha = 0.2)  + # MDR
#  geom_hilight(node = 27, fill = "hotpink", alpha = 0.2) + # Candida
#  geom_hilight(node = 29, fill = "red", alpha = 0.2)     + # albicans
#  geom_hilight(node = 31, fill = "grey20", alpha = 0.2)    # Sacchromycetaceae
ggsave("output/img/20210624-species-tree-side.png", p.tree, width = 3, height = 4.5)

Update 2021-10-22 add new species

The species tree above only includes species with at least one homolog of the Hil family and thus leaves out species that are usually included in the yeast phylogeny. Here I add some of them back to achieve two goals: 1) to help make the point that Candida pathogens have evolved independently multiple times, by adding non-pathogenic species around those that are pathogenic; 2) to enhance the point that the Hil family has experienced losses in the non-pathogenic species.

sps.tree.exp <- read.tree(file = "data/20211017-expanded-species-tree-for-fig1.nwk")
spsData <- read_tsv("data/20211024-extended-species-Als-Hil-homologs-number-table.txt")
Rows: 23 Columns: 5
── Column specification ────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): label, species
dbl (2): Hil, Als
lgl (1): patho

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# correct names
sps.tree.exp <- sps.tree.exp %>% 
  as_tibble() %>% 
  mutate(label = gsub("\\.", "\\. ", label)) %>% 
  left_join(spsData, by = "label") %>% 
  as.treedata()
# plotting
off = 4.5
p <- ggtree(sps.tree.exp, ladderize = FALSE) + xlim(0,16) + scale_y_reverse() +
  geom_tiplab(aes(color = patho), size = 3.5, fontface = "italic", offset = 0.3) +
  scale_color_manual(values = c("black","red"), guide = FALSE) +
  #geom_hilight(node = 27, fill = "magenta", alpha = 0.15) + # Clavispora
  geom_hilight(node = 28, fill = "#7F00FF", alpha = 0.15)  + # MDR
  #geom_hilight(node = 33, fill = "gold", alpha = 0.25) + # Candida
  geom_hilight(node = 35, fill = "pink", alpha = 0.25)     + # albicans
  #geom_hilight(node = 37, fill = "grey50", alpha = 0.15)  + # Sacchromycetaceae
  geom_hilight(node = 41, fill = "steelblue", alpha = 0.15)  + # glabrata
  #geom_cladelabel(node = 27,  label = "Clavispora", offset = off+1.2, color = "orchid3", 
  #                offset.text = 0.3, angle = 270, hjust = .5, extend = 0.3) + # Clavispora
  geom_cladelabel(node = 28,  label = "MDR", offset = off+1, color = "#7F00FF", fontface = 2,
                  offset.text = 0.3, angle = 270, hjust = .5, extend = 0.3) + # MDR
  geom_cladelabel(node = 33,  label = "Candida", offset = off, color = "orange2", barsize = 0.2, 
                  offset.text = 0.3, angle = 270, hjust = .5, extend = 0.3) + # Candida
  geom_cladelabel(node = 35,  label = "albicans", offset = off+1, color = "hotpink2", fontface = 2,
                  offset.text = 0.3, angle = 270, hjust = .5, extend = 0.3) + # albicans
  geom_cladelabel(node = 37,  label = "Saccharomycetaceae", offset = off, color = "grey50",
                  barsize = 0.2,
                  offset.text = 0.3, angle = 270, hjust = .5, extend = 0.3) + # Sacchromycetaceae
  geom_cladelabel(node = 41,  label = "glabrata", offset = off+1, color = "steelblue", fontface = 2,
                  offset.text = 0.3, angle = 270, hjust = .5, extend = 0.3) # glabrata
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
p
ggsave("output/img/20211023-species-tree-multiple-origin.png", width = 5, height = 5)

Also visualize the number of homologs in the Als and Hil families

df0 <- select(spsData, label, Als, Hil) %>%  
  pivot_longer(Hil:Als, names_to = "family", values_to = "number") %>% 
  mutate(species = factor(label, levels = rev(spsData$label)))
p <- ggplot(df0, aes(x = family, y = species)) + 
  geom_tile(aes(fill = number), color = "white", alpha = 0.4) +
  geom_text(aes(label = number), color = "black") +
  scale_fill_distiller(palette = "Greys",direction = 1) +
  scale_x_discrete(position = "top") +
  theme_cowplot() + theme(axis.title = element_blank(), legend.position = "none")
p
ggsave("output/img/20211024-extended-species-Als-Hil-family-size-plot.png", width = 3, height = 5)

Gene tree

RAxML tree before reconciliation

gene.tree <- read.raxml("data/RAxML_bipartitionsBranchLabels.clustalo_3701250") %>% 
  root(node = 143, resolve.root = TRUE, edgelabel = TRUE)
geneNameConvert <- read_tsv("data/20211124-cauris-Hil-gene-name-convert.txt", col_types = "cc")
df0 <- seqInfo %>% select(name, species_id, species_gr) %>% left_join(geneNameConvert, by = "name")
gene.tree <- full_join(gene.tree, df0, by = "label")
sps.color <- c("MDR clade"   = "#7F00FF9F",
               "C. lusitaniae" = "plum2", 
               "D. hansenii" = "gray50",
               "C. parapsilosis" = "orange2",
               "S. stipitis" = "slategray",
               "albicans clade" = "hotpink", 
               "Saccharomycetaceae" = "grey20")
p <- ggtree(gene.tree, aes(alpha = bootstrap), ladderize = TRUE) +
  geom_nodelab(aes(x = branch, label = bootstrap, subset = bootstrap < 80), 
               alpha = 1, vjust = -.5, size = 2) +
  scale_alpha_continuous(name = "Rapid bootstrap", breaks = c(20,60,100)) + 
  geom_tippoint(aes(color = species_gr)) +
  scale_color_manual(name = "Clades / Species", values = sps.color) +
  theme(text = element_text(size = 10))
p <- flip(p, 137,149)
p
ggsave("output/img/20210624-raxml-gene-tree-color.png", width = 7, height = 5)

Fig. 2 RAxML inferred gene tree for Hyr/Iff-Like (HIL) family members in Ascomycetes. The branch length is proportional to the inferred substitions per site. The tree is manually rooted on the Saccharomycetaceae family, which is the outgroup to both the Candida and Clavispora genera and whose Hil homologs form a distinct group. Protein sequence names are not shown for brevity, but are color coded based on the species (groups) they belong to. The clade color code is nested such that if a sequence belongs to both a species and a clade, e.g. C. auris and the MDR clade, the sequence will be colored based on the smaller phylogenetic unit, i.e. C. auris.

Gene tree after reconciliation

We reconciled the gene tree with the species tree in Notung 2.9. The purpose of this step is to reconstruct the history of gene duplications and losses (transfers don’t apply to this case) and to rearrange weakly supported parts of the gene tree to reduce the total event score in accordance with the evolutionary parsimony principal.

Briefly, after both the gene tree and the rooted species tree (see above) were loaded into the graphic user interface, the rooting analysis was run and the branch receiving the highest root score was the same as the manually chosen branch as shown in Fig. 2, namely the ancestral branch for all the Saccharomycetaceae proteins. After rooting and reconciliation, a total of 59 duplications and 47 losses were inferred. The reconciled gene tree was then rearranged. In this step, weakly supported branches – those with rapid bootstrap score < 90% – were allowed to be swapped and the event score, calculated as a weighted sum of the total loss and duplication events, was calculated for each rearrangement. Among all rearranged gene tree topologies, the one with the lowest event score had 42 duplications and 12 losses. This is the gene tree that is shown below.

gene.tree.rec <- read.nhx(file = "output/notung/RAxML_bipartitions_clustalo_3701250_rearrange80.nhx")
gene.tree.rec <- full_join(gene.tree.rec, df0, by = "label")
ggtree(gene.tree.rec, ladderize = FALSE, branch.length = "none") + xlim(0,20) + scale_y_reverse() +
  geom_label(aes(x = branch, label =  ifelse(S %in% c("Saccharomycetaceae", "CUG-Ser1", "DH", "SS") & node != 106, S, NA)),  fill = "gray", size = 3) +
  geom_tiplab(size = 1.5, offset = 0.2) +
  geom_nodepoint(aes(fill = D), shape = 21, color = alpha("grey10", 0)) + scale_fill_manual(values = c("Y" = "red2", "N" = alpha("grey10", 0))) +
  #geom_text(aes(label = ifelse(D == "Y", "D", NA)), hjust = -0.4, size = 2, color = "red") +
  geom_tippoint(aes(color = species_gr)) +
  scale_color_manual(name = "Clades", values = sps.color) +
  theme(text = element_text(size = 14))
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Warning: Removed 200 rows containing missing values (geom_label).
ggsave("output/img/20211124-reconciled-rearranged80-gene-tree-color.png", width = 7.5, height = 7)
Warning: Removed 200 rows containing missing values (geom_label).

Fig. 3 Reconciled and rearranged gene tree for Hyr/Iff-Like (HIL) family members in Ascomycetes. The cladogram shows only the topology of the tree, with endpoints colored in the same way as in Fig. 2. A red “D” next to an internal node indicates an inferred gene duplication event at that node. The labels with gray background highlight the main features of the tree: 1) the Saccharomycetaceae sequences form the outgroup, suggesting there was no ancient duplication prior to the divergence of the family and the remaining species; 2) the CUG-Ser1 clade, which contains both the Candida and Clavispora genera, forms two duplicate groups, suggesting an early duplication event in the clade; 3) the top CUG-Ser1 branch further experienced extensive duplications independently in the Clavispora genus, labeled by the outgroup D. hansenii (DH), and the Candida genus, labeled by the outgroup S. stipitis (SS).

p.gtree <- ggtree(gene.tree.rec, ladderize = FALSE, branch.length = "none") + 
  scale_y_reverse() +
  geom_label(aes(x = branch, label =  ifelse(S %in% c("Saccharomycetaceae", "CUG-Ser1", "DH", "SS") & node != 106, S, NA)),  fill = "gray", size = 3) +
  geom_tippoint(aes(color = species_gr), show.legend = FALSE) +
  scale_color_manual(name = "Clades", values = sps.color) +
  theme(text = element_text(size = 14))
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
ggsave("output/img/20210626-reconciled-gene-tree-side.png", width = 3, height = 6)
Warning: Removed 200 rows containing missing values (geom_label).

export the gene tree order for plotting domain feature maps

gene.tree.rec %>% as_tibble() %>% pull(name) %>% head(104) %>% 
  cat(file = "data/reorder_by_gene_tree.txt", sep = "\n")

Gains and losses on species tree

# read in notung parsed event summary stats
notung.stat <- read_tsv("output/notung/RAxML_bipartitions.clustalo_3701250_rearrange80_event_summary.txt", col_types = 'cii')
sps.tree %>% 
  full_join(notung.stat, by = "label") %>% 
  as_tibble() %>% 
  mutate(label = paste0(str_sub(label, 1, 1), ". ", str_sub(label, 2)),
         patho = c(T,T,T,T,T,F,T,F,T,T,T,F,F,F,T,T,F,T,rep(NA, 35-18))) %>% 
  as.treedata() %>% 
  ggtree(ladderize = FALSE) + scale_y_reverse() +
  geom_tiplab(aes(color = patho), size = 3.5, fontface = "italic", offset = 0.3) + xlim(0,11) +
  scale_color_manual(values = c("black","red"), guide = "none") +
  #geom_tiplab(size = 3.5, fontface = "italic", offset = 0.1) +
  #geom_hilight(node = 22, fill = "magenta", alpha = 0.15) + # Clavispora
  geom_hilight(node = 23, fill = "purple", alpha = 0.15)  + # MDR
  #geom_hilight(node = 27, fill = "gold", alpha = 0.15) + # Candida
  geom_hilight(node = 29, fill = "pink", alpha = 0.25)     + # albicans
  geom_hilight(node = 33, fill = "steelblue", alpha = 0.15)  + # glabrata
  geom_text(aes(label = duplications), hjust = 3, vjust = -.3,
            size = 3, color = "red", fontface = 2) +
  geom_text(aes(label = paste0("/", losses)), hjust = 1.3, vjust = -.3,
            size = 3, color = "grey20", fontface = 2) +
  geom_label(aes(label = ifelse(duplications >= 3, duplications, NA)), hjust = 2.1,
             vjust = -.11, size = 3, color = "red", fill = "yellow1", fontface = 2,
             label.size = 0, label.padding = unit(0.12, "lines"))
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing scale.
Warning: Removed 30 rows containing missing values (geom_label).
ggsave("output/img/20210622-species-tree-with-gains-losses.png", width = 4.5, height = 5)
Warning: Removed 30 rows containing missing values (geom_label).

Fig. 4 Inferred gene duplications and losses in the Hyr/Iff-Like (HIL) family in Ascomycetes. The cladogram shows the species relationship with shadings as in Figure 1. The numbers on top of each branch are the inferred duplications (red) and losses (black) using Notung 2.9. Yellow highlight emphasize the branches that experience three or more duplications.

Conclusions

  1. The Hil family independently expanded in the Candida and Clavispora genera. The most significant expansion occurred within the albicans clade in the Candida genus and the MDR clade in the Clavispora genus.
  2. Many species in the Saccharomycetaceae family have no homologs in this family based on our blast criteria. This suggests that the gene family has contracted and any remaining homologs are shorter than 500 amino acid and thus likely to not play an adhesin function.

Chromosomal locations

While I was performing BLAST on FungiDB to identify homologs of our protein, I noticed that many of the hits appear to be at the beginning and end of the chromosomes. To see if there is a systematic trend in the chromosomal locations, I collected this information for all 99 homologs in the list. Note however, not all genomes are assembled to the chromosomal level, and as a result, the locations for the proteins in those species/strains would be relative to a contig or scaffold. My rationale is that if the contig or scaffold is close to choromosomal length (although that varies by at least an order of magnitude), I could at least look at them separately.

p <- seqInfo %>% 
  mutate(chr = paste(substr(species_id,1,4), chraccver, sep = "_"),
         chr = reorder(chr, chrL, mean)) %>% 
  ggplot(aes(x = chr)) + 
  geom_segment(aes(xend = chr, y = 1, yend = chrL, color = assemblystatus), size = 2) +
  geom_segment(aes(xend = chr, y = chrstart, yend = chrstart+7000), color = "red", size = 3)
p + coord_flip() + theme_classic() + scale_color_manual(values = c("grey50", "grey")) +
  theme(axis.text.y = element_text(size = 5),
        axis.line.y = element_blank(), axis.ticks.y = element_blank(),
        axis.line.x = element_blank(), axis.ticks.x = element_blank(),
        legend.position = c(0.8,0.2),
        panel.background = element_rect(fill = alpha("lightblue",0.1))) +
  labs(y = "Position", x = "Chromosomes / Scaffolds", color = "Assembly Status")
ggsave("output/img/20210213-homologs-chr-loc.png", bg = "transparent", width = 5, height = 5)

p <- seqInfo %>% filter(!(chrL < 1e6 & assemblystatus == "Partial")) %>% 
  mutate(dTip = ifelse(relLoc < 0.5, relLoc, 1-relLoc)) %>% 
  ggplot(aes(x = dTip)) + facet_wrap(~assemblystatus) +
  xlab("Distance from Chromosome or Scaffold Ends") + labs(fill = "Species group") + 
  scale_x_continuous(breaks = seq(0,0.5,by = 0.1))
p + geom_histogram(binwidth = 0.05)

ggsave("output/img/20210213-distribution-on-chromosome-or-scaffolds-histogram-grey.png", width = 5, height = 3)
p + geom_histogram(aes(fill = species_gr), binwidth = 0.05) + scale_fill_brewer(palette = "Paired")

ggsave("output/img/20210213-distribution-on-chromosome-or-scaffolds-histogram.png", width = 6, height = 3)

Discussion

  • Visually there appear to be some enrichment towards the chromosome ends overall.
  • Breakdown by species revealed stronger signals in the albicans clade and the MDR clade.
  • To properly test for enrichment in the chromosomal ends would require documenting the gene density on the affected chromosomes. If the different chromosomes have dramatically different gene distributions, proper tests may not be straightforward to construct. Instead, a bootstrapping approach could be easier to conceive and apply.

Are members of this protein family enriched in the subtelomeric regions?

A recent long-read sequencing study for C. glabrata annotated 31 novel ORFs, of which 24 are GPI-Cell Wall Proteins. The authors cited previous literature supporting the in the subtelomeric regions Xu 2020. This plus the empirical observation above showing an apparent enrichment towards the chromosome ends lead me to ask whether there is indeed a significant enrichment among this family of proteins in the subtelomeric regions.

To formally test this hypothesis, I need to account for the background gene density differences along the chromosomes. The idea is to compare the chromosomal positions for this group of proteins compared with the gene densities on the chromosomes they reside on. We will restrict our analysis to those genomes with a chromosomal level assembly status for obvious reasons.

Update 2021-06-21 We will also include C. auris B11221 as we know its assembly is nearly at the chromosomal level

use.sps <- seqInfo %>% 
  filter(assemblystatus == "Chromosome" | species_id == "Cauris") %>%
  filter(!species_id %in% c("Cdubliniensis", "Ncastellii")) %>%
  # phylogenetic relatedness can induce correlations, including when testing for enrichment at the chromosomal ends
  # based on the gene duplication patterns, combined with YGOB synteny based orthology assignments, the set of species
  # used here, after excluding Cdubliniensis, represent quasi phylogenetically independent contrasts, thanks to the 
  # many species-specific duplications
  select(species_id, assembly) %>% unique()
use.sps
# now let's also create a new tibble for our homologs from these species
fg.freq <- chrLoc %>% 
  filter(species_id %in% use.sps$species_id) %>% 
  # remove the one C. auris gene located on an unassembled fragment
  filter(!(species_id == "Cauris" & chr_name == "scaffold00015")) %>% 
  # change the scaffold name to chromosome name (they correspond)
  mutate(chr_name = gsub("scaffold0+", "", chr_name))

To conduct this test, we first need to prepare and compute the background gene densities. To do this, we will gather the genome assembly files for the selected species, read them into R, and generate a table that contains one row for each gene, with its gene ID, chromosome number, chromosome length and the start position expressed as a percentage measured from the chromosome ends.

# 1. prepare file names
#   get all file names that ends with "feature_table.txt.gz", which contain the gene annotation
feature.files <- list.files(path = "data/assembly-info/", pattern = "*feature_table.txt.gz$")
names(feature.files) <- sapply(str_split(feature.files, pattern = "_"), function(x) {
  paste(x[1], x[2], sep = "_")
})
#   get all file names that ends with "assembly_report.txt", which contain the chromosomal length
assembly.files <- list.files(path = "data/assembly-info/", pattern = "*assembly_report.txt$")
names(assembly.files) <- sapply(str_split(assembly.files, pattern = "_"), function(x) {
  paste(x[1], x[2], sep = "_")
})
use.sps$FeatureFile <- feature.files[use.sps$assembly]
use.sps$AssemblyFile <- assembly.files[use.sps$assembly]

# 2. read in the assembly information
feature.col.names <- c("feature","class","assembly","assembly_unit","seq_type","chromosome","genomic_accession","start","end","strand","product_accession","non_redundant_refseq","related_accession","name","symbol","GeneID","locus_tag","feature_interval_length","product_length","attributes")
assembly.col.names <- c("chromosome","seq_role","assign_molecule","type","gb_acc","relationship",
                        "chraccver","assembly_unit","seqL","ucsc_name")
compute.bg.freq <- function(row){
  assembly.file <- row["AssemblyFile"]
  assembly <- read_tsv(paste0("data/assembly-info/",assembly.file), comment = "#",
                       col_names = assembly.col.names, col_types = "ccccccccic")
  feature.file <- row["FeatureFile"]
  feature <- read_tsv(paste0("data/assembly-info/",feature.file), col_names = feature.col.names,
                      col_types = "ccccccciicccccccciic", skip = 1)
  res <- feature %>% 
    filter(feature == "mRNA") %>% 
    # these feature tables are organized hierarchically, with the top level being "gene"
    # the next level one of "mRNA", "ncRNA", "tRNA" or "rRNA". we only count protein-coding genes, i.e.
    # "mRNA". the reason I didn't select the "CDS" feature type is because in a small number of cases,
    # one mRNA feature contains more than one CDS feature, possibly due to splicing or alternative 
    # translational start site
    select(chromosome, start, end) %>% 
    left_join(select(assembly, chromosome, chraccver, seqL), by = c("chromosome" = "chromosome")) %>%
    mutate(relLoc = round(start / seqL, 3))
  return(res)
}
# apply the function to the genomes, but leave out C. auris
bg.freq <- apply(filter(use.sps, species_id != "Cauris"), MARGIN = 1, compute.bg.freq)
names(bg.freq) <- use.sps %>% filter(species_id != "Cauris") %>% pull(species_id)

Separately calculate the background frequency for C. auris

compute.bg.freq.cau <- function(){
  assembly.col.names <- c("chromosome","seq_role","assign_molecule","type","gb_acc","relationship",
                        "chraccver","assembly_unit","seqL","ucsc_name")
  row = use.sps %>% filter(species_id == "Cauris") # C. auris entry
  assembly.file <- row["AssemblyFile"]
  assembly <- read_tsv(paste0("data/assembly-info/",assembly.file), comment = "#",
                       col_names = assembly.col.names, col_types = "ccccccccic") %>% 
    filter(as.integer(str_sub(chromosome, -2, -1)) <= 7)
  feature.file <- row["FeatureFile"]
  feature <- read_tsv(paste0("data/assembly-info/",feature.file), col_names = feature.col.names,
                      col_types = "ccccccciicccccccciic", skip = 1)
  res <- feature %>% 
    filter(feature == "mRNA") %>% 
    select(chraccver = genomic_accession, start, end) %>% 
    right_join(select(assembly, chromosome, chraccver, seqL), by = "chraccver") %>%
    mutate(relLoc = round(start / seqL, 3),
           chromosome = gsub("scaffold0+","",chromosome))
  return(res)
}
bg.freq$Cauris <- compute.bg.freq.cau()

Let’s take a look at the gene density in one of the genomes, e.g. D. hansenii

We can see that there is typically a drop in gene density towards the chromosome ends, which makes the observation that our protein family are biased towards the chromosomal ends even more striking.

_Note that the above plot was wrong due to the way the density() function estimates the kernel density, which assumes that the data outside the range are possible, in this case smaller than 0 and greater than 1. This leads to the underestimation of the density at the boundaries because it includes regions outside the possible range, where there is no observation. See this post for discussion https://github.com/tidyverse/ggplot2/issues/3387_

Below I use the geom_histogram function instead, with breaks specified manually. This results in a density distribution that is pretty flat across the chromosomes.

In order to compare the distribution of all genes in different bins of the chromosomes to the homologs in our case study, we can divide each chromosome in the nrow(use.sps) genomes into an arbitrary number of bins after “folding” them in half, e.g. 0-10%, 10-20%, 20-30%, 30-40% and 40-50%. To be able to visually compare the distribution of our homologs and the genome background, we will create a special “chromosome” class that will be our homologs and combine them with the bg.freq table.

freq.bins <- c(-0.001, seq(0.1, 0.5, 0.1)); freq.binsL <- c(0, freq.bins[-1])
freq.label <- paste0(head(freq.binsL, -1)*100,"-",tail(freq.binsL, -1)*100,"%")
bg.freq.tb <- bind_rows(bg.freq, .id = "species") %>% 
  mutate(fold.relLoc = ifelse(relLoc <= 0.5, relLoc, 1-relLoc),
         bin = cut(fold.relLoc, breaks = freq.bins, labels = freq.label))

# add the homologs
fg.freq <- fg.freq %>% 
  mutate(fold.relLoc = ifelse(relLoc <= 0.5, relLoc, 1-relLoc),
         bin = cut(fold.relLoc, breaks = freq.bins, labels = freq.label)) 
freq.plot <- fg.freq %>%
  mutate(chromosome = "X") %>%  # we label the homologs as X to make it a separate class
  select(species = species_id, chromosome, bin) %>% 
  bind_rows(select(bg.freq.tb, species, chromosome, bin))# %>% 
  #filter(!species %in% c("Ncastellii")) # remove one species to make it easier to plot

# plot
freq.plot %>% 
  mutate(species = paste0(substr(species, 1, 1), ". ", substr(species, 2, 15))) %>% 
  ggplot(aes(x = chromosome, group = bin, fill = bin)) + 
  geom_bar(position = position_fill()) +
  facet_wrap(~ species, scales = "free_x") + 
  #scale_fill_brewer("Distance from\nchromosome end", type = "qual", palette = 3) +
  scale_fill_viridis_d(direction = -1, end = 0.95, alpha = 0.9) +
  scale_y_continuous(name = "Cumulative % of genes", trans = "reverse", breaks = seq(0,1,0.2)) + 
  theme_cowplot() + panel_border(color = "grey80") +
  theme(legend.position = "none", strip.background = element_blank(),
        strip.text.x = element_text(face = 3))

ggsave("output/img/20210303-compare-homologs-chromosomal-locations-to-bg.png", width = 6, height = 4)

In the plot above, “X” is a special category that collects our homologs. We can see that the distribution of these proteins on the chromosome deviate from the background. Note that a few of the seven species contain only 1-2 PF11765 homologs – these include the three in the middle row – and we should interpret their homologs distribution with caution.

Now that we have the background gene frequencies computed, we can start constructing a test that tests for significant departure in the chromosomal locations of our protein family from the background frequencies. One idea is to divide the chromosome into equal-sized bins and use the frequencies of all genes in each bin as the multinomial probabilities in the null hypothesis. If our proteins were randomly selected from each chromosome without regard to their location, we would expect their locations to conform to the background frequencies. This can be tested using an exact multinomial test or an approximate Chi-square test or G-test. Since we don’t distinguish the two ends of a chromosome, we can “fold” the chromosome “in half” and measure each gene’s location as a percentage from the closer end, i.e. the relative location ranging from 0%-50%. If we can reject the null hypothesis, that constitutes evidence that the proteins from our group are not randomly selected from the background set.

Result of multinomial test

# install XNomial package for the multinomial exact test function
# https://cran.r-project.org/web/packages/XNomial/vignettes/XNomial.html#e1
while(!require(XNomial))
  suppressMessages(install.packages("XNomial"))
Loading required package: XNomial
# calculate pooled background frequency
bg.cnt <- cut(bg.freq.tb$fold.relLoc, breaks = freq.bins, labels = freq.label) %>% tabulate()
fg.cnt <- tabulate(fg.freq$bin)
xmulti(obs = fg.cnt, expr = bg.cnt, detail = 3)

P value  (LLR)  =  3.584e-06
P value (Prob)  =  1.128e-06
P value (Chisq) =  1.408e-07

Observed:  19 4 1 3 2 
Expected ratio:  6573 6731 6868 6842 6859 
Total number of tables:  40920 

Note that the three P-values correspond to three approaches of testing the goodness-of-fit. The LLR, which stands for Log Likelihood Ratio, is generally preferred. But all three said the same thing: the observation deviates from the background frequency significantly. By looking at the plots above, it is clear that the deviation is due to the excess of our homologs residing in the 0-10% bin, which is the tip of the chromosomes.

The test above assumes that the gene density along all chromosomes in the seven species follow the same distribution. The empirical observation supports that hypothesis. Should that to be not the case, we could also account for the variability in gene density distribution between species or even between chromosomes within a species. The idea is to first collect the chromosomes that our homologs come from, and then randomly sample the same number of genes as the number of our homologs on that chromosome. Do this for all of the homolog-containing chromosomes, we would obtain one random sample. Repeat this process 1000 times or more, we will then get the pseudosample, which we can use to compare with our observations. Here we need to come up with a statistic that summarizes each of our observation or pseudosample. For example, we could calculate the median % location for each sample, and ask where our observation lie relative to the pseudosamples. I’ll skip this approach for now since the first appraoch appears to be OK given the similar gene density distribution across chromosomes and species.

LS0tCnRpdGxlOiAiQW5hbHl6ZSBYUF8wMjg4ODkwMzMgZmFtaWx5IGV2b2x1dGlvbiBhbmQgYWRoZXNpbiBwcm9wZXJ0aWVzIgphdXRob3I6ICJCaW4gSGUiCmRhdGU6ICIxMS8wMS8yMDIwIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6IAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0b2NfZGVwdGg6IDUKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgZmlnLmFzcDogMQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKYGBge3IgbG9hZF9saWJyYXJpZXMsIGVjaG8gPSBGQUxTRX0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJ0cmVlaW8iLCBxdWlldGx5ID0gVFJVRSkpCiAgICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgidHJlZWlvIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJnZ3RyZWUiLCBxdWlldGx5ID0gVFJVRSkpCiAgICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZ2d0cmVlIikKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJHZW5vbWljUmFuZ2VzIiwgcXVpZXRseSA9IFRSVUUpKQogICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIkdlbm9taWNSYW5nZXMiKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKbGlicmFyeShnZ3RyZWUpCmxpYnJhcnkodHJlZWlvKQpyZXF1aXJlKEdlbm9taWNSYW5nZXMpCmBgYAoKIyBHb2FsCgpBbmFseXplIHRoZSBhZGhlc2luIHByb3BlcnRpZXMgb2YgdGhlIFhQXzAyODg4OTAzMyBob21vbG9ncyAocHV0YXRpdmUgYWRoZXNpbiBpbiBfQy4gYXVyaXNfKQpUaGlzIGlzIHZlcnNpb24gMyBvZiB0aGUgYW5hbHlzaXMsIHVzaW5nIHRoZSB1cGRhdGVkIGhvbW9sb2dzIG9uIDIwMjEtMDItMDUKCiMgQnVpbGQgZGF0YXNldHMKIyMgQmFzaWMgaW5mb3JtYXRpb24KRmlyc3QgZ2V0IHRoZSBiYXNpYyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgMTAwIHNlcXVlbmNlcyBpbiB0aGlzIHN0dWR5LiBJIGRlY2lkZSB0byB3cml0ZSBhIHNpbXBsZSBQeXRob24gc2NyaXB0IHRvIGV4dHJhY3Qgc3VjaCBpbmZvLgpgYGBiYXNoCiMgZWRpdCB0aGUgUFlUSE9OIHBhdGggYmVsb3cgdG8gbWF0Y2ggeW91ciBsb2NhbCBzeXN0ZW0Kfi9zdy9taW5pY29uZGEzL2Jpbi9weXRob24gZXh0cmFjdF9zZXFfaW5mby5weQpgYGAKCkxvYWQgaW4gdGhlIHNlcXVlbmNlIGluZm9ybWF0aW9uLgoKPiBub3RlIHRoYXQgSSBuZWVkIHRvIG1hbnVhbGx5IGVkaXQgdGhlIHNlcXVlbmNlIGlkcyBmb3IgZm91ciBzZXF1ZW5jZXMgZnJvbSBmdW5naWRiLCBiZWNhdXNlIEkgdXNlZCB0aGVpciByZWZzZXFfaWQgd2hlbiByZXRyaWV2aW5nIHRoZWlyIGNocm9tb3NvbWFsIGxvY2F0aW9ucy4KCnwgR1JZQyBJRCB8IFJlZnNlcV9JRHwKfC0tLS0tLS0tLXwtLS0tLS0tLS0tfAp8IENMVUdfMDUyMzMgICB8IFhQXzAwMjYxNTIxOC4xIHwKfCBDUEFSMl82MDA0MzAgfCBYUF8wMzY2NjI4MTUuMSB8CnwgQ1BBUjJfODA2MzkwIHwgWFBfMDM2NjY1MjYyLjEgfAp8IENQQVIyXzgwNjQyMCB8IFhQXzAzNjY2NTI2NS4xIHwKCmBgYHtyIGxvYWRfc2VxX2luZm99CnNwcy5saXN0IDwtIGMoIkNkdW9idXNoYWVtdWxvbmlzIiwiQ3BzZXVkb2hhZW11bG9uaXMiLCJDaGFlbXVsb25pIiwiQ2F1cmlzIiwiQ2x1c2l0YW5pYWUiLCJEaGFuc2VuaWkiLCJDcGFyYXBzaWxvc2lzIiwiTGVsb25naXNwb3J1cyIsIkN0cm9waWNhbGlzIiwiQ2R1YmxpbmllbnNpcyIsIkNhbGJpY2FucyIsIlNzdGlwaXRpcyIsIktsYWN0aXMiLCJOY2FzdGVsbGlpIiwiQ2dsYWJyYXRhIiwiTmJyYWNhcmVuc2lzIiwiTmRlbHBoZW5zaXMiLCJObml2YXJpZW5zaXMiKQpzZXFJbmZvIDwtIHJlYWRfdHN2KCJkYXRhL1hQXzAyODg4OTAzM19ob21vbG9ncy50c3YiLCBjb21tZW50ID0gIiMiLCBjb2xfdHlwZXMgPSAiY2NjaSIpICU+JSAKICBtdXRhdGUoc3BlY2llc19pZCA9IGZhY3RvcihzcGVjaWVzLCBsZXZlbHMgPSBzcHMubGlzdCksIHNwZWNpZXMgPSBOVUxMKQpjaHJMb2MgPC0gcmVhZF90c3YoImRhdGEvWFBfMDI4ODg5MDMzX2hvbW9sb2dzX2Nocl9sb2MudHN2IiwgY29sX3R5cGVzID0gY29scygpKSAlPiUgCiAgbXV0YXRlKGlkID0gaWZlbHNlKGlzLm5hKGFjY2Vzc2lvbiksIGdlbmVfaWQsIGFjY2Vzc2lvbiksIAogICAgICAgICBhY2Nlc3Npb24gPSBOVUxMLCAKICAgICAgICAgc3BlY2llc19pZCA9IGZhY3RvcihzcGVjaWVzX2lkLCBsZXZlbHMgPSBzcHMubGlzdCksCiAgICAgICAgIHJlbExvYyA9IHJvdW5kKGNocnN0YXJ0L2NockwsIDMpKQoKc2VxSW5mbyA8LSBzZXFJbmZvICU+JSAKICBsZWZ0X2pvaW4oY2hyTG9jKSAlPiUgCiAgbXV0YXRlKGFzc2VtYmx5c3RhdHVzID0gZmFjdG9yKGFzc2VtYmx5c3RhdHVzLCBsZXZlbHMgPSBjKCJDb21wbGV0ZSBHZW5vbWUiLCJDaHJvbW9zb21lIiwiQ29udGlnIiwiU2NhZmZvbGQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIkNocm9tb3NvbWUiLCAiQ2hyb21vc29tZSIsICJQYXJ0aWFsIiwgIlBhcnRpYWwiKSksCiAgICAgICAgIHNwZWNpZXNfZ3IgPSBmYWN0b3Ioc3BlY2llc19pZCwgbGFiZWxzID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjKHJlcCgiTURSIGNsYWRlIiw0KSwiQy4gbHVzaXRhbmlhZSIsIkQuIGhhbnNlbmlpIixyZXAoIkMuIHBhcmFwc2lsb3NpcyIsMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcCgiYWxiaWNhbnMgY2xhZGUiLDMpLCAiUy4gc3RpcGl0aXMiLCByZXAoIlNhY2NoYXJvbXljZXRhY2VhZSIsNikpKSkgJT4lIAogIHNlbGVjdChuYW1lLCBpZCwgc291cmNlLCBzdGFydHNfd2l0aCgiZ2VuZSIpLCBzdGFydHNfd2l0aCgic3BlY2llcyIpLCBzdHJhaW4sIHN0YXJ0c193aXRoKCJhc3NlbWJseSIpLAogICAgICAgICBsZW5ndGgsIG5fc2Vxcywgc3RhcnRzX3dpdGgoImNociIpLCByZWxMb2MpCmBgYAoKIyMgQWRoZXNpbiBwcmVkaWN0aW9ucwpbRnVuZ2FsUlZdKGh0dHA6Ly9mdW5nYWxydi5pZ2liLnJlcy5pbi8pIGFuZCBbRmFhUHJlZF0oaHR0cDovL2Jpb2luZm8uaWNnZWIucmVzLmluL2ZhYXAvKSBhcmUgdHdvIFN1cHBvcnQgVmVjdG9yIE1hY2hpbmUgKFNWTSkgYmFzZWQgcHJlZGljdGlvbiBhbGdvcml0aG1zIHRoYXQgdXNlIHNlcXVlbmNlIGZlYXR1cmVzIHN1Y2ggYXMgYW1pbm8gYWNpZCBjb21wb3NpdGlvbiAoZnJlcXVlbmN5LCBwaHlzaW9jaGVtaWNhbCBwcm9wZXJ0aWVzIGV0Yy4pIGFzIGlucHV0IGFuZCB0cmFpbiBNYWNoaW5lIExlYXJuaW5nIG1vZGVscyB0byBkaXN0aW5ndWlzaCBmdW5nYWwgYWRoZXNpbnMgZnJvbSBub24tYWRoZXNpbnMuCmBgYHtyIGFkaGVzaW5fcHJlZGljdGlvbn0KZnJ2LnRoID0gMC41MTEgIyByZWNvbW1lbmRlZCBGdW5nYWxSViBzY29yZSB0aHJlc2hvbGQKZnJ2IDwtIHJlYWRfdHN2KCJvdXRwdXQvRnVuZ2FsUlYvZnVuZ2FsUlZfcmVzdWx0LnR4dCIsIHNraXAgPSAzLCBjb2xfbmFtZXMgPSBjKCJuYW1lIiwiZnJ2LnNjb3JlIiksIGNvbF90eXBlcyA9ICJjZCIpICU+JSAKICBtdXRhdGUobmFtZSA9IHN0cl9zdWIobmFtZSwgMiksIGZydi5wcmVkID0gZnJ2LnNjb3JlID4gZnJ2LnRoKQpmYWEgPC0gcmVhZF90c3YoIm91dHB1dC93ZWItZG93bmxvYWQvZmFhcHJlZF9yZXN1bHQudHh0IiwgY29sX25hbWVzID0gYygibmFtZSIsImZhYS5zY29yZSIsImZhYS5wcmVkIiksIGNvbF90eXBlcyA9ICJjZGMiKSAlPiUgCiAgbXV0YXRlKGZhYS5wcmVkID0gaWZlbHNlKGZhYS5wcmVkID09ICJBZGhlc2luIiwgVFJVRSwgRkFMU0UpKQppZigiZnJ2LnNjb3JlIiAlaW4lIG5hbWVzKHNlcUluZm8pKQogIHNlcUluZm8gPC0gc2VsZWN0KHNlcUluZm8sIC1mcnYuc2NvcmUsIC1mcnYucHJlZCwgLWZhYS5zY29yZSwgLWZhYS5wcmVkKQpzZXFJbmZvIDwtIHNlcUluZm8gJT4lIGxlZnRfam9pbihmcnYpICU+JSBsZWZ0X2pvaW4oZmFhKQpgYGAKCmBgYHtyfQpkZiA8LSBzZXFJbmZvICU+JSAKICBncm91cF9ieShzcGVjaWVzX2lkKSAlPiUgCiAgc3VtbWFyaXplKG4gPSBuKCksIAogICAgICAgICAgICBmdW5nYWxSViA9IHN1bShmcnYuc2NvcmUgPiAwLjUxMSksIGZhYXByZWQgPSBzdW0oZmFhLnByZWQsIG5hLnJtID0gVCksIAogICAgICAgICAgICBib3RoID0gc3VtKGZydi5zY29yZSA+IDAuNTExICYgZmFhLnByZWQpLAogICAgICAgICAgICBtZWFuLmZydiA9IHJvdW5kKG1lYW4oZnJ2LnNjb3JlKSwgMiksIG1lYW4uZmFhID0gcm91bmQobWVhbihmYWEuc2NvcmUsIG5hLnJtID0gVCksIDIpKQoKZHQgPC0gRFQ6OmRhdGF0YWJsZShkZiwgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ3QnLCBwYWdlTGVuZ3RoID0gMjAsIG9yZGVyaW5nID0gRkFMU0UpLCAKICAgICAgICAgICAgICAgIGNhcHRpb24gPSAiU3VtbWFyeSBvZiBBZGhlc2luIFByZWRpY3Rpb24gUmVzdWx0cyIpCiMgdGhlIGZvbGxvd2luZyBjb2RlIGlzIGZyb20gaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vMjAyMC8wNS9taW1pYy1leGNlbHMtY29uZGl0aW9uYWwtZm9ybWF0dGluZy1pbi1yLTIvCmJya3MgPC0gc3RhdHM6OnF1YW50aWxlKHggPC0gZGZbWzJdXSwgcHJvYnMgPSBzZXEoLjA1LCAuOTUsIC4wNSksIG5hLnJtID0gVFJVRSkKI2NscnMgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjZmNkYmRiIiwgIiNmZjE1MTUiKSkobGVuZ3RoKGJya3MpKzEpCmNscnMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKG4gPSA0LCBuYW1lID0gIlJlZHMiKSkobGVuZ3RoKGJya3MpKzEpCmZvciAoaiBpbiAyOjUpewogICMgQ3JlYXRlIGJyZWFrcyBmb3Igc2hhZGluZyBjb2x1bW4gdmFsdWVzIGhpZ2ggdG8gbG93CiAgIyBDcmVhdGUgc2hhZGVzIG9mIGdyZWVuIGZvciBiYWNrZ3JvdW5kcwogICMgRm9ybWF0IGNlbGxzIGluIGotdGggY29sdW1uCiAgZHQgPC0gRFQ6OmZvcm1hdFN0eWxlKGR0LCBqLCBiYWNrZ3JvdW5kQ29sb3IgPSBEVDo6c3R5bGVJbnRlcnZhbChicmtzLCBjbHJzKSkKfQpgYGAKYGBge3J9CiMgc2VlIGh0dHBzOi8veXVsYWItc211LnRvcC90cmVlZGF0YS1ib29rL2NoYXB0ZXI3Lmh0bWwjYXR0YWNoLW9wZXJhdG9yCiMgdGhlIGF0dGFjaCBvcGVyYXRvciAiJTwrJSIgd29ya3Mgb24gZ2d0cmVlIG9iamVjdHMgYW5kIHJlcXVpcmVzIHRoZSBmaXJzdCBjb2x1bW4KIyBvZiB0aGUgZGF0YSBmcmFtZSB0byBjb250YWluIHRoZSB0aXAgbGFiZWxzCmBgYAoKIyMgR1BJLWFuY2hvciBwcmVkaWN0aW9uCkdQSS1hbmNob3JlZCBwcm90ZWlucyBhcmUgY2hhcmFjdGVyaXplZCBieSBhbiBOLXRlcm1pbmFsIHNpZ25hbCBwZXB0aWRlLCB3aGljaCB3b3VsZCBkaXJlY3QgdGhlIHByb3RlaW4gdG8gdGhlIHNlY3JldGFyeSBwYXRod2F5LCBhbmQgYSBDLXRlcm1pbmFsIEdQSS1hbmNob3IgcGVwdGlkZSwgd2hpY2ggd291bGQgYmUgY2xlYXZlZCBhbmQgcmVwbGFjZWQgYnkgdGhlIEdQSS1hbmNob3IsIGFsbG93aW5nIHRoZSBwcm90ZWluIHRvIGJlIHRldGhlcmVkIHRvIHRoZSBjZWxsIHdhbGwuCgpGb3Igc2lnbmFsIHBlcHRpZGUsIEkgdXNlZCBTaWduYWxQIHNlcnZlci4gSXRzIGxhdGVzdCB2ZXJzaW9uIGlzIDUuMC4gQnV0IEkgYWxzbyByYW4gdGhlIHNlcXVlbmNlcyB0aHJvdWdoIHRoZWlyIDQuMSB2ZXJzaW9uLCB3aXRoIHR3byBzZXR0aW5ncy4gVGhlIHJlc3VsdHMgb2YgdGhlIGxhdHRlciB0d28gYXJlIGFsbW9zdCBpZGVudGljYWwsIGV4Y2VwdCBmb3Igb25lIHNlcXVlbmNlICJYUF8wMjQ3MTEzNTAuMSIsIHdoaWNoIGlzIG9ubHkgaW5jbHVkZWQgaW4gdGhlIHNlbnNpdGl2ZSB2ZXJzaW9uLCBhbmQgaGFzIGEgcHJvYmFiaWxpdHkgbG93ZXIgdGhhbiAwLjUuCgpgYGB7ciBzaWduYWxQLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01fQojIFNpZ25hbCBwZXB0aWRlCmdmZi5uYW1lcyA8LSBjKCJpZCIsICJzb3VyY2UiLCAibmFtZSIsICJzdGFydCIsICJlbmQiLCAicHJvYiIsICJuYTEiLCAibmEyIiwgIm5hMyIpCnNpZ25hbHA1IDwtIHJlYWRfdHN2KCJvdXRwdXQvd2ViLWRvd25sb2FkL3NpZ25hbHBfNS4wLmdmZiIsIGNvbW1lbnQgPSAiIyIsIGNvbF9uYW1lcyA9IGdmZi5uYW1lcywgY29sX3R5cGVzID0gImNjY2lpZGNjYyIpCgppZigic2lnbmFscCIgJWluJSBuYW1lcyhzZXFJbmZvKSkKICBzZXFJbmZvIDwtIHNlbGVjdChzZXFJbmZvLCAtc2lnbmFscCkKCnNlcUluZm8gPC0gbGVmdF9qb2luKHNlcUluZm8sIHNlbGVjdChzaWduYWxwNSwgbmFtZSA9IGlkLCBwcm9iKSwgYnkgPSBjKCJuYW1lIiA9ICJuYW1lIikpICU+JSAKICBtdXRhdGUoc2lnbmFscCA9ICFpcy5uYShwcm9iKSkgJT4lIHNlbGVjdCgtcHJvYikKYGBgCgpGb3IgR1BJLWFuY2hvciBwcmVkaWN0aW9uLCBJIHVzZWQgdGhlIFtQcmVkR1BJIHNlcnZlcl0oaHR0cDovL2dwY3IuYmlvY29tcC51bmliby5pdC9wcmVkZ3BpLykuCmBgYHtyIGdwaX0KdG1wIDwtIHJlYWRfZGVsaW0oIm91dHB1dC93ZWItZG93bmxvYWQvcHJlZGdwaV9yZXN1bHQudHh0IiwgZGVsaW0gPSAifCIsIGNvbF9uYW1lcyA9IGMoIm5hbWUiLCJmcCIsIm9tZWdhIikpCnByZWQuZ3BpIDwtIHRtcCAlPiUgCiAgbXV0YXRlKG5hbWUgPSBzdHJfc3ViKG5hbWUsMiwtMiksICMgcmVtb3ZlID4gYW5kIHRoZSB0cmFpbGluZyBzcGFjZQogICAgICAgICBmcCA9IGFzLm51bWVyaWMoc3RyX3N1YihmcCwgOSwgLTIpKSwgIyBleHRyYWN0IHRoZSBudW1lcmljIHBhcnQKICAgICAgICAgaXMuZ3BpID0gZnAgPD0gMC4wMSwgICAgIyBiYXNlZCBvbiB0aGUgY3V0b2ZmIG9mIHRoZSBQcmVkR1BJIHNlcnZlciAocHJvYiA8IDk5JSAtPiBub3QgR1BJLWFuY2hvcmVkKQogICAgICAgICBvbWVnYSA9IHN0cl9zdWIob21lZ2EsIDgpLAogICAgICAgICBjbGVhdmVSZXMgPSBzdHJfc3ViKG9tZWdhLCAxLCAxKSwKICAgICAgICAgY2xlYXZlUG9zID0gYXMuaW50ZWdlcihzdHJfc3ViKG9tZWdhLCAzKSkKICAgICAgICAgKSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChzZXFJbmZvLCBuYW1lLCBsZW5ndGgpLCBieSA9IGMoIm5hbWUiID0gIm5hbWUiKSkKCiMgcmVtb3ZlIHRoZSBjb2x1bW4gaWYgaXQgYWxyZWFkeSBleGlzdHMKaWYoInByZWQuZ3BpIiAlaW4lIG5hbWVzKHNlcUluZm8pKQogIHNlcUluZm8gPC0gc2VsZWN0KHNlcUluZm8sIC1wcmVkLmdwaSkKc2VxSW5mbyA8LSBsZWZ0X2pvaW4oc2VxSW5mbywgc2VsZWN0KHByZWQuZ3BpLCBuYW1lLCBwcmVkLmdwaSA9IGlzLmdwaSksIGJ5ID0gYygibmFtZSI9Im5hbWUiKSkKYGBgCmBgYHtyfQpkZjEgPC0gc2VxSW5mbyAlPiUgCiAgZ3JvdXBfYnkoc3BlY2llc19pZCkgJT4lIAogIHN1bW1hcml6ZShTaWduYWxQID0gc3VtKHNpZ25hbHApLCBHUEkgPSBzdW0ocHJlZC5ncGkpLCBBbGwgPSBzdW0oZnJ2LnNjb3JlID4gMC41MTEgJiBzaWduYWxwICYgcHJlZC5ncGkpKSAlPiUgCiAgcmlnaHRfam9pbihkZikgJT4lIAogIHNlbGVjdChzcGVjaWVzID0gc3BlY2llc19pZCwgVG90YWwgPSBuLCBGUlYgPSBmdW5nYWxSViwgU1AgPSBTaWduYWxQLCBHUEksIEFsbCkgJT4lIAogIHBpdm90X2xvbmdlcihUb3RhbDpBbGwsIG5hbWVzX3RvID0gInR5cGUiLCB2YWx1ZXNfdG8gPSAibnVtYmVyIikgJT4lIAogIG11dGF0ZSh0eXBlID0gZmFjdG9yKHR5cGUsIGxldmVscyA9IHVuaXF1ZSh0eXBlKSkpCnAgPC0gZ2dwbG90KGRmMSwgYWVzKHggPSB0eXBlLCB5ID0gc3BlY2llcykpICsgCiAgc2NhbGVfeV9kaXNjcmV0ZShsaW1pdHMgPSByZXYpICsKICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBudW1iZXIpLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC40KSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG51bWJlciksIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDQpICsKICBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIkdyZXlzIixkaXJlY3Rpb24gPSAxKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShwb3NpdGlvbiA9ICJ0b3AiKSArCiAgdGhlbWVfY293cGxvdCgpICsgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpwCmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIxMTAyNC1ob21vbG9ncy1wcmVkaWN0aW9uLXN1bW1hcnkucG5nIiwgd2lkdGggPSA0LjUsIGhlaWdodCA9IDUpCmBgYAoKKipTdW1tYXJ5KioKCi0gTW9zdCBvZiB0aGUgSGlsIGhvbW9sb2dzIGluIEFzY29teWNldGVzIHBhc3NlZCBib3RoIHRoZSBHUEktYW5jaG9yIHByZWRpY3Rpb25zIGFuZCBhbHNvIHRoZSBGdW5nYWxSViBwcmVkaWN0b3IsIHN1Z2dlc3RpbmcgdGhhdCB0aGV5IGFyZSBsaWtlbHkgdG8gYmUgY2VsbCB3YWxsIHByb3RlaW5zIGFuZCBtYXkgcGxheSBhbiBhZGhlc2luIGZ1bmN0aW9uIChGYWFQcmVkIHByZWRpY3RlZCBmZXdlciBhZGhlc2lucywgYnV0IHRoZSBvdmVyYWxsIHBhdHRlcm4gZG9lc24ndCBjaGFuZ2UpLgoKIyMgVGFuZGVtIHJlcGVhdCBzdHJ1Y3R1cmVzClRoZSBub24tTlREIHBvcnRpb24gb2YgdGhlIHByb3RlaW5zIGV2b2x2ZSByYXBpZGx5IGFuZCBtYW55IG9mIHRoZW0gY29udGFpbiB0YW5kZW0gcmVwZWF0cy4gVGhlcmVmb3JlLCBjaGFyYWN0ZXJpemluZyBhbmQgdmlzdWFsaXppbmcgdGhlIHR5cGUsIG51bWJlciBhbmQgc3BhdGlhbCBkaXN0cmlidXRpb24gb2YgdGhlIHRhbmRlbSByZXBlYXRzIHNlcnZlIHRvIGhpZ2hsaWdodCB0aGUgZGlmZmVyZW5jZXMgaW4gdGhlIG5vbi1OVEQgcGFydCBvZiB0aGUgcHJvdGVpbnMgaW4gdGhpcyBmYW1pbHkuCgpUbyBpZGVudGlmeSBhbmQgZ3JvdXAgdGFuZGVtIHJlcGVhdHMsIEkgdXNlZCBbWFNUUkVBTV0oaHR0cHM6Ly9hbW5ld21hbmxhYi5zdGFuZm9yZC5lZHUveHN0cmVhbSkgd2l0aCB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgYGphdmEgLVhteDEwMDBtIC1YbXMxMDAwbSAtamFyIH4vc3cvWFNUUkVBTS94c3RyZWFtLmphciAkaW4gLWkuNyAtSS43IC1nMyAtZTIgLUwxNSAteiAtRyAtTyAtQXN1Yi50eHRgLiBUaGUgcGFyYW1ldGVycyB3ZXJlIGNob3NlbiB0byBpZGVudGlmeSBkZWdlbmVyYXRlIHRhbmRlbSByZXBlYXRzIHRoYXQgb2NjdXIgYXQgbGVhc3QgdHdvIHRpbWVzIGFuZCBtdXN0IGJlIGEgbWluaW11bSBsZW5ndGggb2YgNSBhLmEuIG9yIGxvbmdlciBhbmQgdGhlIG1pbmltdW0gbGVuZ3RoIG9mIGEgdGFuZGVtIHJlcGVhdCBkb21haW4gKD1wZXJpb2QgeCBjb3B5ICMpIG11c3QgYmUgZ3JlYXRlciB0aGFuIDE1IGEuYS4gUGxlYXNlIHNlZSBgc2NyaXB0L3hzdHJlYW0uc2hgIGZvciBleHBsYW5hdGlvbiBvZiB0aGUgcGFyYW1ldGVycy4KCmBgYHtyfQp0YW5kZW0gPC0gcmVhZF90c3YoIm91dHB1dC94c3RyZWFtL1hTVFJFQU1fWFBfMDI4ODg5MDMzX2hvbW9sb2dzX3N1Yl9pMC43X2czX201X0wxNV9jaGFydC50c3YiLAogICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gImNpaWlmaWRjY2NkIiwgY29tbWVudCA9ICIjIikKIyBub3cgbGV0J3MgY3JlYXRlIGEgdGliYmxlIGZvciBwbG90dGluZywgd2hpY2ggd291bGQgY29udGFpbiBlYWNoIGluc3RhbmNlIG9mIHRoZSB0YW5kZW0gcmVwZWF0IG9uIGEgc2VwYXJhdGUgcm93CnRhbmRlbS5kaXYgPC0gdGFuZGVtICU+JSAKICByb3d3aXNlKG5hbWUpICU+JSAKICBzdW1tYXJpemUoZGl2ID0gbGlzdChjKHNlcShmcm9tID0gc3RhcnQsIHRvID0gZW5kLCBieSA9IHBlcmlvZCksIGVuZCkpLCAuZ3JvdXBzID0gImRyb3AiKSAlPiUgCiAgdW5uZXN0KGRpdikKCiMgc3VtbWFyaXplIHN0YXRzIG9mIHRhbmRlbSByZXBlYXRzCnJlcGVhdHMgPC0gdGFuZGVtICU+JSAKICBncm91cF9ieSh0eXBlLCBwZXJpb2QpICU+JSAKICBzdW1tYXJpemUobiA9IG4oKSwgY29weU1lYW4gPSBtZWFuKGNvcHlOKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lIAogIG11dGF0ZShsZW5ndGggPSBwZXJpb2QgKiBjb3B5TWVhbikKYGBgCgpDYWxjdWxhdGUgbGVuZ3RoIG9mIHRhbmRlbSByZXBlYXRzIGFzIGEgcGVyY2VudGFnZSBvZiB0aGUgcHJvdGVpbiBsZW5ndGggbWludXMgdGhlIFBGMTE3NjUgZG9tYWluCgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NH0KIyB0YW5kZW0gcmVwZWF0IGxlbmd0aCBkaXN0cmlidXRpb24KZ2dwbG90KHJlcGVhdHMsIGFlcyhsZW5ndGgpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSA0MCkgKyAKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygxMCwyMCw0MCw4MCwxNjAsMzIwLDY0MCwxMjgwKSwgdHJhbnMgPSAibG9nMiIpICsKICB0aGVtZV9jb3dwbG90KCkKCiMgdGFuZGVtIHJlcGVhdCBsZW5ndGggY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24KZ2dwbG90KHJlcGVhdHMsIGFlcyhsZW5ndGgpKSArIHN0YXRfZWNkZihnZW9tID0gInN0ZXAiKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSAxMCoyXnNlcV9sZW4oOSksIHRyYW5zID0gImxvZzIiKSArCiAgeWxhYigiUHJvYmFiaWxpdHkiKSArIHhsYWIoImxlbmd0aCAoPSBwZXJpb2QgKiBtZWFuIGNvcGllcykiKSArIAogIHRoZW1lX2Nvd3Bsb3QoKSArIHBhbmVsX2JvcmRlcihjb2xvciA9ICJibGFjayIpICMrIGJhY2tncm91bmRfZ3JpZCgpCmBgYAoKIyBGZWF0dXJlIG1hcCBmb3IgaG9tb2xvZ3MKVGhlIGdvYWwgaXMgdG8gcHJvZHVjZSBhIGNhcnRvb24tbGlrZSBwbG90IGZvciBlYWNoIGhvbW9sb2cgb3V0bGluaW5nIHRoZWlyIG1haW4gZmVhdHVyZXMsIHN1Y2ggYXMgdGhlIGxvY2F0aW9ucyBvZiB0aGUgUEZhbSBkb21haW5zIChtYWlubHkgdGhlIEh5cF9yZWdfQ1dQKSwgbG9jYXRpb25zIG9mIHRoZSBzaWduYWwgcGVwdGlkZSBhbmQgR1BJLWFuY2hvciwgZGlzdHJpYnV0aW9uIG9mIFRBTkdPIHNlcXVlbmNlcy4gTm90ZSB0aGF0IGFsbCB0aGVzZSBmZWF0dXJlcyBjYW4gYmUgcmVwcmVzZW50ZWQgYXMgYSByYW5nZSB3aXRoIGFzc29jaWF0ZWQgbWV0YWRhdGEuIFNvIHRoZSBmaXJzdCBzdGVwIGlzIHRvIGNvbGxlY3QgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBmZWF0dXJlcwoKIyMgTG9hZCBQZmFtIGRvbWFpbnMKYGBge3J9CiMgUGZhbSBkb21haW5zCnBmYW0gPC0gcmVhZF90c3YoIm91dHB1dC93ZWItZG93bmxvYWQvSE1NRVItSE1NU2Nhbi1QZmFtLWhpdHMudHN2IiwgY29sX3R5cGVzID0gImNpaWlpY2NpaWlkZGRpaWMiKQojIHNhdmUgZmVhdHVyZSBmaWxlIGZvciBKYWx2aWV3IGV4YW1pbmF0aW9uCiMgcGZhbSAlPiUgZmlsdGVyKGdyZXBsKCJYUF8wMjg4ODkwMzMiLHNlcV9pZCkpICU+JSBzZWxlY3QoaG1tX25hbWUsIHNlcV9pZCwgZW52ZWxvcGVfc3RhcnQsIGVudmVsb3BlX2VuZCkgJT4lIG11dGF0ZShmZWF0dXJldHlwZSA9ICJkb21haW4iKSAlPiUgd3JpdGVfdHN2KCJYUF8wMjg4ODkwMzNfZmVhdHVyZXMuamFsdmlldyIpCiMgSSBtYW51YWxseSBlZGl0ZWQgdGhlIGZlYXR1cmUgZmlsZSwgc28gSSBjb21tZW50ZWQgb3V0IHRoZSBsaW5lIGFib3ZlIHRvIGF2b2lkIGFjY2lkZW50YWxseSAKIyBvdmVyd3JpdGluZyBteSBvd24gZWRpdHMKYGBgCgojIyBPcmdhbml6ZSBhbmQgY29tYmluZSB0aGUgdGFuZGVtIHJlcGVhdHMgZmVhdHVyZXMKYGBge3J9CnRyIDwtIHRhbmRlbSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChyZXBlYXRzLCB0eXBlLCBjb3B5TWVhbiksIGJ5ID0gYygidHlwZSIgPSAidHlwZSIpKSAlPiUgCiAgbXV0YXRlKHR5cGUgPSAiVGFuZGVtIFJlcGVhdHMiKSAlPiUgCiAgc2VsZWN0KG5hbWUsIHR5cGUsIHN0YXJ0LCBlbmQpCmBgYAoKYGBge3J9CiMgR1BJLWFuY2hvcgojIHVzZSBwcmVkLmdwaQojIGZlYXR1cmUgc2V0CiMgc3RydWN0dXJlOiBpZCAgZmVhdHVyZSAgc3RhcnQgIGVuZApzZXFMZW4gPC0gc2VxSW5mbyAlPiUgbXV0YXRlKHN0YXJ0ID0gMSkgJT4lIHNlbGVjdChuYW1lLCBzdGFydCwgZW5kID0gbGVuZ3RoKQpmZWF0dXJlIDwtIGJpbmRfcm93cygKICBwZmFtICU+JSBzZWxlY3QobmFtZSA9IHNlcV9pZCwgdHlwZSA9IGhtbV9uYW1lLCBzdGFydCA9IGVudmVsb3BlX3N0YXJ0LCBlbmQgPSBlbnZlbG9wZV9lbmQpICU+JSAKICAgIGZpbHRlcih0eXBlID09ICJIeXBoYWxfcmVnX0NXUCIpLAogICMgZXh0ZW5kIHRoZSBzaWduYWwgcGVwdGlkZSBzZWdtZW50IGJ5IDEwIGFtaW5vIGFjaWRzIHRvIG1ha2UgaXQgbW9yZSB2aXNpYmxlCiAgc2lnbmFscDUgJT4lIG11dGF0ZSh0eXBlID0gIlNpZ25hbCBQZXB0aWRlIiwgZW5kID0gZW5kICsgMTApICU+JSBzZWxlY3QobmFtZSA9IGlkLCB0eXBlLCBzdGFydCA9IHN0YXJ0LCBlbmQpLAogICMgZXh0ZW5kIHRoZSBHUEktYW5jaG9yIEMtdGVybWludXMgc2VnbWVudCBieSAyMCBhbWlubyBhY2lkcyB0byBtYWtlIGl0IG1vcmUgdmlzaWJsZQogIHByZWQuZ3BpICU+JSBmaWx0ZXIoaXMuZ3BpKSAlPiUgbXV0YXRlKHR5cGUgPSAiR1BJLWFuY2hvciIsIHN0YXJ0ID0gY2xlYXZlUG9zLTEwKSAlPiUgCiAgICBzZWxlY3QobmFtZSwgdHlwZSwgc3RhcnQsIGVuZCA9IGxlbmd0aCksCiAgdHIKKSAKCiMgaW4gb3JkZXIgdG8gcGxvdCBwcm9wZXJ0aWVzIG9mIHRoZSBzZXF1ZW5jZXMgaW4gYW4gb3JkZXIgdGhhdCBpcyBjb25zaXN0ZW50IHdpdGggdGhlIHNlcXVlbmNlcycgcG9zaXRpb24gaW4gdGhlIGdlbmUgdHJlZQpnZW5ldHJlZU9yZGVyIDwtIHNjYW4oImRhdGEvcmVvcmRlcl9ieV9nZW5lX3RyZWUudHh0Iiwgd2hhdCA9ICJjaGFyYWN0ZXIiKQpzZXFMZW4kbmFtZSA8LSBvcmRlcmVkKHNlcUxlbiRuYW1lLCBsZXZlbHMgPSByZXYoZ2VuZXRyZWVPcmRlcikpCgpmZWF0dXJlIDwtIGZlYXR1cmUgJT4lIAogIG11dGF0ZShuYW1lID0gb3JkZXJlZChuYW1lLCBsZXZlbHMgPSByZXYoZ2VuZXRyZWVPcmRlcikpLAogICAgICAgICB0eXBlID0gb3JkZXJlZCh0eXBlLCBsZXZlbHMgPSBjKCJlbnRpcmUgcHJvdGVpbiIsICJIeXBoYWxfcmVnX0NXUCIsICJTaWduYWwgUGVwdGlkZSIsICJHUEktYW5jaG9yIiwgIlRhbmRlbSBSZXBlYXRzIikpKQpmZWF0dXJlLmNvbG9ycyA8LSBjKEh5cGhhbF9yZWdfQ1dQID0gIiMzZDg1YzYiLCAiU2lnbmFsIFBlcHRpZGUiID0gIiNjYzAwMDAiLCAiR1BJLWFuY2hvciIgPSAiIzZhM2Q5YSIsICJUYW5kZW0gUmVwZWF0cyIgPSAiI2FmODQwMGJiIikKYGBgCiMjIFBsb3QgZG9tYWluIG9yZ2FuaXphdGlvbgpgYGB7ciBwbG90X3RhbmRlbV9yZXBlYXRzLCB3YXJuaW5nPUZBTFNFfQojIHBsb3QKcDAgPC0gZ2dwbG90KHNlcUxlbiwgYWVzKHggPSBuYW1lLCB5ID0gc3RhcnQsIHhlbmQgPSBuYW1lLCB5ZW5kID0gZW5kKSkgKyAKICAjZ2VvbV9zZWdtZW50KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIuMSkgKwogIGdlb21fc2VnbWVudChjb2xvciA9ICJncmF5ODAiLCBzaXplID0gMikKcDEgPC0gZ2VvbV9zZWdtZW50KGRhdGEgPSBmZWF0dXJlLCBhZXMoY29sb3IgPSB0eXBlKSwgc2l6ZSA9IDIpCnAyIDwtIGdlb21fc2VnbWVudChkYXRhID0gdGFuZGVtLmRpdiwgYWVzKHggPSBuYW1lLCB4ZW5kID0gbmFtZSwgeSA9IGRpdiwgeWVuZCA9IGRpdiArIDEuNSksIHNpemUgPSAyLCBjb2xvciA9ICJ3aGl0ZSIpCnAzIDwtIHAwICsgcDEgKyBjb29yZF9mbGlwKCkgKyB0aGVtZV9jbGFzc2ljKCkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gZmVhdHVyZS5jb2xvcnMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAuMDIsIDAuMDIpKSkgKwogIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA0KSwgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2JsYW5rKCksIyBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjc1LCAwLjM1KSwgCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSksIAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IGFscGhhKCJsaWdodGJsdWUiLDAuMSkpKSArCiAgbGFicyh5ID0gIlBvc2l0aW9uIGluIHNlcXVlbmNlIiwgeCA9ICJTZXF1ZW5jZXMiLCBjb2xvciA9ICJGRUFUVVJFUyIpCiNwbG90X2dyaWQocC5ndHJlZSwgcDMsIG5jb2wgPSAyLCBhbGlnbiA9ICd2JywgcmVsX3dpZHRocyA9IGMoMSwyKSwgc2NhbGUgPSBjKDEuMDEsMSkpCmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIxMDYyNS1kb21haW4tdGFuZGVtLXJlcGVhdHMucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA3LjUpCmBgYAoqKkZpZy4gPyoqIEJsdWUgYm94ZXMgaW5kaWNhdGUgdGhlIFBGMTE3NjUgZG9tYWlucyB3aGlsZSBhbGwgb3RoZXIgbm9uLWdyZXkgYm94ZXMgaW5kaWNhdGUgWFNUUkVBTS1kZXRlcm1pbmVkIHRhbmRlbSByZXBlYXQgZG9tYWlucy4gQ29sb3JzIGFyZSB1c2VkIHRvIGdyb3VwIGhpZ2hseSBzaW1pbGFyIHRhbmRlbSByZXBlYXRzLiBUaGUgYmxhY2sgdGhpbiBsaW5lcyBkZW1hcmNhdGUgYWRqYWNlbnQgdGFuZGVtIHJlcGVhdCB1bml0cy4gVGhlIHRhYmxlIGJlbG93IHNob3dzIHRoZSBjb3B5IG51bWJlciwgcGVyaW9kIGFuZCBjb25zZW5zdXMgc2VxdWVuY2UgZm9yIGVhY2ggdGFuZGVtIGRvbWFpbiBvcmdhbml6ZWQgYnkgdGhlIGhvc3Qgc2VxdWVuY2VzLgoKIyMgU2VwYXJhdGUgVFIgdHlwZXMKYGBge3J9CkRUOjpkYXRhdGFibGUoCiAgdGFuZGVtICU+JSAKICAgIGRwbHlyOjpyZW5hbWUoc2VxTCA9IHNlcUxlbmd0aCwgZXJyID0gY29uc2Vuc3VzX2Vycm9yLCBzZXEgPSBjb25zZW5zdXNfbm9nYXApICU+JSAKICAgIHNlbGVjdCgtc2VxQWxpZ24sIC10eXBlLCAtY29uc2Vuc3VzX2dhcCwgLXNlcSwgc2VxKSAlPiUgCiAgICBhcnJhbmdlKGRlc2MobmFtZSkpLAogIGZpbGxDb250YWluZXIgPSBGQUxTRSwgb3B0aW9ucyA9IGxpc3QocGFnZUxlbmd0aCA9IDEwKQopCmBgYAoKUmVwZWF0IHRoZSBhYm92ZSBhbmFseXNpcyBidXQgZGlzdGluZ3Vpc2hpbmcgYmV0d2VlbiBhbGwgZGlmZmVyZW50IFRSIHR5cGVzCmBgYHtyfQp0cjEgPC0gdGFuZGVtICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KHJlcGVhdHMsIHR5cGUsIGNvcHlNZWFuKSwgYnkgPSBjKCJ0eXBlIiA9ICJ0eXBlIikpICU+JSAKICBtdXRhdGUodHlwZSA9IHBhc3RlMCgiVFItIiwgdHlwZSksCiAgICAgICAgIHRpcCA9IHBhc3RlMChjb25zZW5zdXNfbm9nYXAsCiAgICAgICAgICAgICAgICAgICAgICAiXG50eXBlOiAiLCB0eXBlLAogICAgICAgICAgICAgICAgICAgICAgIlxucGVyaW9kOiAiLCBwZXJpb2QsIAogICAgICAgICAgICAgICAgICAgICAgIlxuY29weU46ICIsIGNvcHlNZWFuKSwKICAgICAgICAgbmFtZSA9IG9yZGVyZWQobmFtZSwgbGV2ZWxzID0gcmV2KGdlbmV0cmVlT3JkZXIpKSkgJT4lIAogIHNlbGVjdChuYW1lLCB0eXBlLCBzdGFydCwgZW5kLCB0aXApCmBgYAoKYGBge3Igc2V0X3RyX2NvbG9yfQpyZXF1aXJlKFJDb2xvckJyZXdlcikKdHIudGggPC0gMTAwICMgYXJiaXRyYXJ5IHRocmVzaG9sZCBmb3IgZGlzdGluZ3Vpc2hpbmcgInNob3J0IiBmcm9tICJsb25nIiBUUnMKdHIuY29sIDwtIGNoYXJhY3Rlcihucm93KHJlcGVhdHMpKSAjIGNyZWF0ZSBhIGNvbG9yIHZlY3RvcgpzaG9ydC5ycCA8LSB3aGljaChyZXBlYXRzJGxlbmd0aCA8IHRyLnRoKSAjIGlkZW50aWZ5IHRoZSBzaG9ydCByZXBlYXRzIGluZGljZXMKbG9uZy5ycCA8LSBzZXRkaWZmKDE6bnJvdyhyZXBlYXRzKSwgc2hvcnQucnApICMgdGhlIGxvbmcgcmVwZWF0cyBpbmRpY2VzCnNldC5zZWVkKDEyMykgIyBmb3IgcmVwcm9kdWNpYmx5IHNodWZmbGluZyB0aGUgb3JkZXIgYmVmb3JlIGFzc2lnbmluZyB0aGUgY29sb3JzCnNob3J0LnJwIDwtIHNhbXBsZShzaG9ydC5ycCkgIyBzaHVmZmxlIHRoZSBpbmRpY2VzIGZvciBzaG9ydCB0YW5kZW0gcmVwZWF0cwp0ci5jb2xbc2hvcnQucnBdIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpW3NlcSgzLDExLGJ5PTIpXSkobGVuZ3RoKHNob3J0LnJwKSkgIyBhc3NpZ24gdGhlIHNob3J0IHJlcGVhdHMgYSBsb3dlciBjb250cmFzdCBjb2xvcgpzZXQuc2VlZCgyMzEpICMgZm9yIHJlcHJvZHVjaWJseSBzaHVmZmxpbmcgdGhlIG9yZGVyIGJlZm9yZSBhc3NpZ25pbmcgdGhlIGNvbG9ycwpsb25nLnJwIDwtIHNhbXBsZShsb25nLnJwKQp0ci5jb2xbbG9uZy5ycF0gPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDEyLCAiUGFpcmVkIilbc2VxKDQsMTIsYnk9MildKShsZW5ndGgobG9uZy5ycCkpICMgYXNzaWduIHRoZSBsb25nIHJlcGVhdHMgYSBoaWdoZXIgY29udHJhc3QgY29sb3IKIyAtLSBkZXNhdHVyYXRlIHRoZSBjb2xvcnMgLS0gCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMjYzMTQ3MDEvci1yZWR1Y2luZy1jb2xvdXItc2F0dXJhdGlvbi1vZi1hLWNvbG91ci1wYWxldHRlCmxpYnJhcnkoY29sb3JzcGFjZSkgICAjIyBoc3YgY29sb3JzcGFjZSBtYW5pcHVsYXRpb25zCgojIyBGdW5jdGlvbiBmb3IgZGVzYXR1cmF0aW5nIGNvbG9ycyBieSBzcGVjaWZpZWQgcHJvcG9ydGlvbgpkZXNhdCA8LSBmdW5jdGlvbihjb2xzLCBzYXQ9MC41KSB7CiAgICBYIDwtIGRpYWcoYygxLCBzYXQsIDEpKSAlKiUgcmdiMmhzdihjb2wycmdiKGNvbHMpKQogICAgaHN2KFhbMSxdLCBYWzIsXSwgWFszLF0pCn0KCnRyLmNvbCA8LSBkZXNhdCh0ci5jb2wsIHNhdCA9IDAuOCkKIyAtLSBmaW5pc2ggLS0KdHIuY29sIDwtIHBhc3RlMCh0ci5jb2wsICJDQyIpICMgYWRkIDIwJSB0cmFuc3BhcmVuY3kgdG8gdGhlIFRSIGZlYXR1cmVzCm5hbWVzKHRyLmNvbCkgPC0gcGFzdGUwKCJUUi0iLCByZXBlYXRzJHR5cGUpICMgbmFtZSB0aGUgY29sb3JzIGJ5IHRoZSBUUiB0eXBlcwpyZXBlYXRzJGNvbG9yIDwtIHRyLmNvbApgYGAKCkNvbWJpbmUgZG9tYWlucywgU1AgYW5kIEdQSS1hbmNob3Igd2l0aCBUUiBmZWF0dXJlcy4KYGBge3J9IAojIGNvbWJpbmUgc2VxdWVuY2UgZmVhdHVyZXMgd2l0aCB0YW5kZW0gcmVwZWF0cwpmZWF0dXJlMSA8LSBiaW5kX3Jvd3MoZmlsdGVyKGZlYXR1cmUsIHR5cGUgIT0gIlRhbmRlbSBSZXBlYXRzIiksIHRyMSkgJT4lIAogIG11dGF0ZSh0eXBlID0gb3JkZXJlZCh0eXBlLCBsZXZlbHMgPSBjKCJIeXBoYWxfcmVnX0NXUCIsICJTaWduYWxQIiwgIkdQSS1hbmNob3IiLCB1bmlxdWUodHIxJHR5cGUpKSkpCmZlYXR1cmUuY29sMSA8LSBjKGZlYXR1cmUuY29sb3JzWzE6M10sIHRyLmNvbCkgIyByZW1vdmUgdGhlIHNpbmdlbCAiVGFuZGVtIFJlcGVhdHMiIHR5cGUKd3JpdGVfdHN2KGZlYXR1cmUxLCBmaWxlID0gIm91dHB1dC9taXNjL1ItZmVhdHVyZS10YWJsZS50c3YiLCBjb2xfbmFtZXMgPSBUUlVFKQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojIHBsb3QKcDAgPC0gZ2dwbG90KHNlcUxlbiwgYWVzKHggPSBuYW1lLCB5ID0gc3RhcnQsIHhlbmQgPSBuYW1lLCB5ZW5kID0gZW5kKSkgKyAKICAjZ2VvbV9zZWdtZW50KGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDIuMSkgKwogIGdlb21fc2VnbWVudChjb2xvciA9ICJncmF5ODAiLCBzaXplID0gMikKcDEgPC0gZ2VvbV9zZWdtZW50KGRhdGEgPSBmZWF0dXJlMSwgYWVzKGNvbG9yID0gdHlwZSwgdGV4dCA9IHRpcCksIHNpemUgPSAyKQpwMiA8LSBnZW9tX3NlZ21lbnQoZGF0YSA9IHRhbmRlbS5kaXYsIGFlcyh4ID0gbmFtZSwgeGVuZCA9IG5hbWUsIHkgPSBkaXYsIHllbmQgPSBkaXYgKyAxLjUpLCBzaXplID0gMiwgY29sb3IgPSAid2hpdGUiKQpwMyA8LSBwMCArIHAxICsgY29vcmRfZmxpcCgpICsgdGhlbWVfY2xhc3NpYygpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGZlYXR1cmUuY29sMSkgKwogIHNjYWxlX3lfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMC4wMiwgMC4wMikpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDQpLCBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfYmxhbmsoKSwjIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IGFscGhhKCJsaWdodGJsdWUiLDAuMSkpKSArCiAgbGFicyh5ID0gIlBvc2l0aW9uIGluIHNlcXVlbmNlIiwgeCA9ICJTZXF1ZW5jZXMiLCBjb2xvciA9ICJGRUFUVVJFUyIpCiMgcGxvdF9ncmlkKHAuZ3RyZWUsIHAzICsgcDIsIG5jb2wgPSAyLCBhbGlnbiA9ICd2JywgcmVsX3dpZHRocyA9IGMoMSwyKSwgc2NhbGUgPSBjKDEuMDEsMSkpCmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIxMDYyNi1kb21haW4tdGFuZGVtLXJlcGVhdHMtZGlzdGluY3QtY29sb3IucG5nIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA3LjUpCmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9OSwgZmlnLndpZHRoPTl9CiMgcGxvdAojcmVxdWlyZShwbG90bHkpCmdncGxvdGx5KHAzLCB0b29sdGlwID0gInRleHQiLCB3aWR0aCA9IDkwMCwgaGVpZ2h0ID0gOTAwKQpgYGAKIyBTaW1pbGFyaXR5IGFuZCBkaXZlcmdlbmNlCiMjIExlbmd0aCBkaXN0cmlidXRpb24KYGBge3IgcGxvdF9wcm90X2xlbmd0aH0KIyBwbG90IHRoZSBsZW5ndGggZGlzdHJpYnV0aW9uIG9mIHRoZSBob21vbG9ncyBpbiBlYWNoIHNwZWNpZXMKcCA8LSBnZ3Bsb3Qoc2VxSW5mbywgYWVzKHggPSBzcGVjaWVzX2lkLCB5ID0gbGVuZ3RoKSkgKyAKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDUwMCwgbGluZXR5cGUgPSAyKSArCiAgI3N0YXRfc3VtbWFyeShmdW4uZGF0YSA9ICJtZWFuX2NsX2Jvb3QiLCBjb2xvciA9ICJyZWQiKSArCiAgZ2VvbV9kb3RwbG90KGJpbndpZHRoID0gMTAwLCBiaW5heGlzID0gInkiLCBzdGFja2RpciA9ICJjZW50ZXIiLCBkb3RzaXplID0gMC41LCAKICAgICAgICAgICAgICAgYmlucG9zaXRpb25zID0gImFsbCIsIGZpbGwgPSByZ2IoMCwwLDAsMC41KSwgY29sb3IgPSByZ2IoMCwwLDAsMC42KSkgKyAKICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IHJldikgKwogIGNvb3JkX2ZsaXAoKSArICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBYUF8wMjg4ODkwMzMgaG9tb2xvZ3MnIGxlbmd0aCIpICsKICB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKQogIApwbG90X2dyaWQocC50cmVlLCBwLCBhbGlnbiA9ICJoIiwgbmNvbCA9IDIsIHJlbF93aWR0aHMgPSBjKDEsMS41KSwgc2NhbGUgPSBjKDEuMDgsMSkpCmBgYAoqKkZpZy4gNSBEaXN0cmlidXRpb24gb2YgSGlsIGZhbWlseSBob21vbG9ncycgbGVuZ3RoIGFjcm9zcyBzcGVjaWVzLioqIFRoZSBzaGFkaW5ncyBpbiB0aGUgc3BlY2llcyB0cmVlIG9uIHRoZSBsZWZ0IGlzIHRoZSBzYW1lIGFzIGluIEZpZ3VyZSAxLiBQcm90ZWluIGxlbmd0aHMgYXJlIHBsb3R0ZWQgYm90aCBhcyBhIGRvdHBsb3QgKGJpbm5lZCBpbiAxMDAgYS5hLiBiaW5zKSBhbmQgYSBib3hwbG90LCB3aGVyZSB0aGUgYm94IHJlcHJlc2VudHMgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UsIHRoZSBtaWRkbGUgYmFyIGluZGljYXRlcyB0aGUgbWVkaWFuIGFuZCB0aGUgd2hpc2tlciAxLjUgdGltZXMgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UuCgpfKipEaXNjdXNzaW9uKipfCgotIFRoZSBvbmUgc2VxdWVuY2UgYmVsb3cgNTAwIGEuYS4gaXMgZnJvbSBfTi4gZGVscGhlbnNpc18sIHdoaWNoIGlzIGxhYmVsZWQgYXMgYSBwYXJ0aWFsIENEUy4KLSBNYWpvcml0eSBvZiB0aGUgcHJvdGVpbnMgaW4gdGhlIGxpc3QgYXJlIDUwMC0yMDAwIGEuYS4sIHdpdGggYSBmZXcgZXhjZXB0aW9uYWxseSBsb25nCi0gTm90IG9ubHkgZG8gU2FjY2hhcm9teWNldGFjZWFlIHNwZWNpZXMgaGF2ZSBmZXdlciBIaWwgZmFtaWx5IGhvbW9sb2dzLCB0aGUgb25lcyB0aGV5IGhhdmUgYXJlIGFsc28gc2hvcnQgKDwgMTAwMCBhLmEuKSB3aXRoIHRoZSBleGNlcHRpb24gb2YgX0MuIGdsYWJyYXRhXwoKIyMgUHJvdGVpbiBsZW5ndGggdnMgdGFuZGVtIHJlcGVhdCBjb250ZW50CkV4YW1pbmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSBlbnRpcmUgcHJvdGVpbiBsZW5ndGggYW5kIHRhbmRlbSByZXBlYXQgY29udGVudApgYGB7cn0KIyBjYWxjdWxhdGUgdGhlIGxlbmd0aCBvZiB0aGUgUEYxMTc2NSBkb21haW4gaW4gZWFjaCBwcm90ZWluCnBmMTE3NjUubGVuZ3RoIDwtIGZlYXR1cmUgJT4lIAogIGZpbHRlcih0eXBlID09ICJIeXBoYWxfcmVnX0NXUCIpICU+JSAKICBtdXRhdGUoZG9tYWluTGVuID0gZW5kIC0gc3RhcnQgKyAxKSAlPiUgCiAgIyBuZWVkIHRoZSBmb2xsb3dpbmcgc3RlcCBiZWNhdXNlIG9uZSBwcm90ZWluIGhhcyB0d28gaW5zdGFuY2VzIG9mIFBGMTE3NjUKICBncm91cF9ieShuYW1lKSAlPiUgCiAgc3VtbWFyaXplKGRtTGVuID0gc3VtKGRvbWFpbkxlbikpCgojIGNhbGN1bGF0ZSByZXBlYXQgY29udGVudAp0ci5sZW5ndGggPC0gdGFuZGVtICU+JSAKICBtdXRhdGUodHJMZW5ndGggPSBlbmQgLSBzdGFydCArIDEpICU+JSAKICBncm91cF9ieShuYW1lLCBzZXFMZW5ndGgpICU+JSAKICBzdW1tYXJpemUodHJMZW4gPSBzdW0odHJMZW5ndGgpKSAlPiUgCiAgZHBseXI6OnJlbmFtZShsZW5ndGggPSBzZXFMZW5ndGgpICU+JSAKICByaWdodF9qb2luKHNlbGVjdChzZXFJbmZvLCBuYW1lLCBsZW5ndGgsIHNwZWNpZXNfaWQsIHNwZWNpZXNfZ3IpLAogICAgICAgICAgICAgYnkgPSBjKCJuYW1lIiwibGVuZ3RoIikpICU+JSAKICBtdXRhdGUodHJMZW4gPSBpZmVsc2UoaXMubmEodHJMZW4pLCAwLCB0ckxlbikpICU+JSAKICBsZWZ0X2pvaW4ocGYxMTc2NS5sZW5ndGgsIGJ5ID0gIm5hbWUiKSAlPiUgCiAgbXV0YXRlKG5vbk5URExlbiA9IGxlbmd0aCAtIGRtTGVuLCB0ci5jb250ZW50ID0gdHJMZW4vbm9uTlRETGVuKQpgYGAKCmBgYHtyfQp0ci5sZW5ndGggJT4lIAogIGdncGxvdChhZXMoeCA9IHRyTGVuLCB5ID0gbGVuZ3RoKSkgKyAKICBnZW9tX3BvaW50KHNpemUgPSAyLjUsIGFscGhhID0gMC43KSArIAogICNnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgI3NjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIkNsYWRlIiwgdmFsdWVzID0gc3BzLmNvbG9yKSArCiAgeWxhYigiUHJvdGVpbiBsZW5ndGgiKSArIHhsYWIoIlRhbmRlbSByZXBlYXQgbGVuZ3RoIikgKyB5bGltKDAsIDQ1MDApICsKICB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiYmxhY2siKQpnZ3NhdmUoIm91dHB1dC9pbWcvMjAyMTA2MjctcHJvdGVpbi1sZW5ndGgtZXZvbC1kcml2ZW4tYnktdGFuZGVtLXJlcGVhdHMucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA0LjUpCmBgYApTaG9ydGVyIHByb3RlaW5zIHNlZW0gdG8gaGF2ZSBhIGxvd2VyIGNvbnRlbnQgb2YgdGFuZGVtIHJlcGVhdHMgYXMgYSBwZXJjZW50cmFnZSBvZiB0aGVpciBub24gTlREIHBvcnRpb24gb2YgdGhlIHByb3RlaW4uCmBgYHtyfQp0ci5sZW5ndGggJT4lIAogIG11dGF0ZShncm91cCA9IGN1dChsZW5ndGgsIGJyZWFrcyA9IGMoMCwxMDAwLDE1MDAsNTAwMCksIGxhYmVscyA9IGMoIjwxMDAwIiwiPDE1MDAiLCI+MTUwMCIpKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGdyb3VwLCB5ID0gdHIuY29udGVudCkpICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSkgKyBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMiwgc2l6ZSA9IDIsIGFscGhhID0gMC44KSArCiAgI2dlb21faGxpbmUoeWludGVyY2VwdCA9IDAuNSwgbGluZXR5cGUgPSAyLCBjb2xvciA9ICJncmF5MzAiKSArCiAgI2dlb21fdmxpbmUoeGludGVyY2VwdCA9IDE1MDAsIGxpbmV0eXBlID0gMiwgY29sb3IgPSAiZ3JheTMwIikgKwogICNzY2FsZV9jb2xvcl9tYW51YWwobmFtZSA9ICJDbGFkZSIsIHZhbHVlcyA9IHNwcy5jb2xvcikgKwogIHlsYWIoIlRhbmRlbSByZXBlYXQgJSIpICsgeGxhYigiUHJvdGVpbiBsZW5ndGgiKSArICNjb29yZF9mbGlwKCkgKwogIHRoZW1lX2Nvd3Bsb3QoZm9udF9zaXplID0gMTYpICsgcGFuZWxfYm9yZGVyKGNvbG9yID0gImJsYWNrIikKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjExMTIwLXRhbmRlbS1yZXBlYXQtcHJvcG9ydGlvbi5wbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMpCmBgYApgYGB7cn0KIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy83NTQ5Njk0L2FkZC1yZWdyZXNzaW9uLWxpbmUtZXF1YXRpb24tYW5kLXIyLW9uLWdyYXBoCiMgIyBHRVQgRVFVQVRJT04gQU5EIFItU1FVQVJFRCBBUyBTVFJJTkcKIyAjIFNPVVJDRTogaHR0cHM6Ly9ncm91cHMuZ29vZ2xlLmNvbS9mb3J1bS8jIXRvcGljL2dncGxvdDIvMVRnSC1rRzVYTUEKIyBtb2RpZmllZCBieSBCaW4gSGUgMjAyMS0wNi0yOApsbV9lcSA8LSBmdW5jdGlvbihtKXsKICAgIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKHkpID09IGEgKyBiICUuJSBpdGFsaWMoeCkqIiwifn5pdGFsaWMocileMn4iPSJ+cjIsIAogICAgICAgICBsaXN0KGEgPSBmb3JtYXQodW5uYW1lKGNvZWYobSlbMV0pLCBkaWdpdHMgPSA0KSwKICAgICAgICAgICAgICBiID0gZm9ybWF0KHVubmFtZShjb2VmKG0pWzJdKSwgZGlnaXRzID0gNCksCiAgICAgICAgICAgICByMiA9IGZvcm1hdChzdW1tYXJ5KG0pJHIuc3F1YXJlZCwgZGlnaXRzID0gMykpKQogICAgYXMuY2hhcmFjdGVyKGFzLmV4cHJlc3Npb24oZXEpKTsKfQoKbG0ub2JqIDwtIGxtKG5vbk5URExlbiB+IHRyTGVuLCB0ci5sZW5ndGgpCgp0ci5sZW5ndGggJT4lIAogIGdncGxvdChhZXMoeCA9IHRyTGVuLCB5ID0gbm9uTlRETGVuKSkgKyAKICBnZW9tX3Ntb290aChtZXRob2QgPSAiZ2xtIiwgZm9ybXVsYSA9IHl+eCwgc2UgPSBGQUxTRSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDIuNSwgYWxwaGEgPSAwLjcpICsgCiAgYW5ub3RhdGUoZ2VvbSA9ICJ0ZXh0IiwgeCA9IDIwMDAsIHkgPSA1MDAsIGxhYmVsID0gbG1fZXEobG0ub2JqKSwgcGFyc2UgPSBUUlVFLCBzaXplID0gNSkgKwogIHhsYWIoIlRhbmRlbSByZXBlYXQgbGVuZ3RoIikgKyB5bGFiKCJMZW5ndGggb2Ygbm9uLU5URCBwYXJ0IikgKwogIHRoZW1lX2Nvd3Bsb3QoKSArIHBhbmVsX2JvcmRlcihjb2xvciA9ICJibGFjayIpCmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIxMDYyOC1wcm90ZWluLW5vbk5URC1sZW5ndGgtZHJpdmVuLWJ5LXRhbmRlbS1yZXBlYXQucG5nIiwgd2lkdGggPSA1LCBoZWlnaHQgPSA0LjUpCmBgYAoKXyoqRGlzY3Vzc2lvbioqXwoKLSBUYW5kZW0gcmVwZWF0IGNvbnRlbnQgaXMgaGlnaGx5IGNvcnJlbGF0ZWQgdG8gdGhlIG5vbi1OVEQgcG9ydGlvbiBvZiB0aGUgcHJvdGVpbiBsZW5ndGgsIHdpdGggYW4gZXN0aW1hdGVkIHNsb3BlIG9mIGNsb3NlIHRvIDEsIHN1Z2dlc3RpbmcgdGhhdCBwcm90ZWluIGxlbmd0aCBldm9sdXRpb24gaXMgc3Ryb25nbHkgZHJpdmVuIGJ5IHRoZSBhY3F1aXJlbWVudCwgZXhwYW5zaW9uIG9yIGNvbnRyYWN0aW9uIG9mIHRhbmRlbSByZXBlYXRzLgotIEhvbW9sb2dzIHNob3J0ZXIgdGhhbiAxMzAwIGFtaW5vIGFjaWRzIGluIHRoZSBub24tTlREIHBvcnRpb24gaGF2ZSBtb3JlIHZhcmlhYmlsaXR5IGluIHRoZSB0YW5kZW0gcmVwZWF0IHByb3BvcnRpb24sIHdpdGggc29tZSBoYXZpbmcgdmVyeSBsaXR0bGUgdGFuZGVtIHJlcGVhdHMuCgojIyBTZXJpbmUvVGhyZW9uaW5lIGNvbnRlbnQKClMvVCBzaXRlcyBhcmUgcG90ZW50aWFsIHNpdGVzIGZvciBPLWdseWNvc3lsYXRpb24sIHdoaWNoIGNvdWxkIGluY3JlYXNlIHRoZSByaWRpZGl0eSBvZiB0aGUgc3RhbGsgb2YgdGhlIHByb3RlaW4gYW5kIGFsbG93IHRoZSBOLXRlcm1pbmFsIGRvbWFpbiB0byBwcm90cnVkZSBvdXQgb2YgdGhlIGNlbGwgd2FsbCBmYWNpbmcgdGhlIGV4dGVyaW9yLiBNb3JlIGV2aWRlbmNlIGZvciB0aGUgaW1wb3J0YW5jZSBvZiBPLWdseWNvc3lsYXRpb24gaW4gYSBzZXJpbmUvdGhyZW9uaW5lLXJpY2ggZG9tYWluIGNhbiBiZSBmb3VuZCBbaGVyZV0oaHR0cHM6Ly9lYy5hc20ub3JnL2NvbnRlbnQvMTAvMTAvMTMxNy5sb25nKS4KCiMjIyBJbiB3aG9sZSBwcm90ZWluClRoZSBnb2FsIGhlcmUgaXMgdG8gY29tcGFyZSB0aGUgUy9UIGZyZXF1ZW5jaWVzIGluIHRoZSBIaWwgcHJvdGVpbnMgdG8gdGhlIHByb3Rlb21lIGF2ZXJhZ2UuIEZpcnN0IHdlIG5lZWQgdG8gY2FsY3VsYXRlIHRoZSBTL1QgZnJlcXVlbmNpZXMgaW4gdGhlIEhpbCBwcm90ZWlucywgZWl0aGVyIGluIHRoZSB3aG9sZSBwcm90ZWluIG9yIGV4Y2x1ZGluZyB0aGUgUEYxMTc2NSBkb21haW4uIFRoaXMgaXMgZG9uZSB3aXRoIGEgc2NyaXB0IGluIHRoZSBgc2NyaXB0YCBmb2xkZXIgbmFtZWQgYEhpbC1TVC1mcmVxLnNoYAoKVG8gaGVscCB3aXRoIHRoZSBsYXR0ZXIgdGFzayAoaW4gdGhlIG5vbi1QRjExNzY1IGRvbWFpbiBwYXJ0KSwgSSBleHBvcnQgdGhlIFBGMTE3NjUgZG9tYWluIGJvdW5kYXJpZXMgYXMgYSBCRUQgZm9ybWF0IGZpbGUKYGBge3J9CmZlYXR1cmUgJT4lIAogIGZpbHRlcih0eXBlID09ICJIeXBoYWxfcmVnX0NXUCIpICU+JSAKICBzZWxlY3QobmFtZSwgc3RhcnQsIGVuZCkgJT4lIAogIHdyaXRlX3RzdihmaWxlID0gImRhdGEvWFBfMDI4ODg5MDMzX2hvbW9sb2dzX1BGMTE3NjUuQkVEIiwgY29sX25hbWVzID0gRkFMU0UpCmBgYAoKUmVhZCBpbiB0aGUgUy9UIGZyZXF1ZW5jaWVzCmBgYHtyfQpTVC5mdWxsIDwtIHJlYWRfdHN2KCJvdXRwdXQvU1QtZnJlcS8yMDIxMTEyMS1IaWwtZnVsbC1TVGZyZXEub3V0IiwgY29sX3R5cGVzID0gImNpaWkiKQpTVC5ub05URCA8LSByZWFkX3Rzdigib3V0cHV0L1NULWZyZXEvMjAyMTExMjEtSGlsLW5vUEYxMTc2NS1TVGZyZXEub3V0IiwgY29sX3R5cGVzID0gImNpaWkiKQpgYGAKClRvIGRldGVybWluZSB0aGUgYmFja2dyb3VuZCBmcmVxdWVuY3kgb2YgU2VyaW5lIGFuZCBUaHJlb25pbmUgaW4gdGhlIHByb3Rlb21lKHMpLCBJIG1vZGlmaWVkIHRoZSBgY2FsY19hYWZyZXFfZ3oucHlgIHNjcmlwdCBJIHdyb3RlIGEgbG9uZyB0aW1lIGFnbyBmb3IgY2FsY3VsYXRpbmcgdGhlIGN5c3RlaW4gYW5kIGRpYmFzaWMgcmVzaWR1ZXMuIEkgdGhlbiBjb3BpZWQgZm91ciBwcm90ZW9tZSBmYXN0YSBmaWxlcywgZm9yIF9DLiBhbGJpY2Fuc18sIF9DLiBnbGFicmF0YV8sIF9TLiBjZXJldmlzaWFlXyBhbmQgX0MuIGF1cmlzXyBhbmQgYXBwbGllZCB0aGUgc2NyaXB0IG9uIHRoZW0gKHVzaW5nIGEgd3JhcHBlciBzY3JpcHQgY2FsbGVkIGBTLVQtZnJlcS5zaGApLiBCZWxvdyBJIHdpbGwgbG9vayBhdCBib3RoIHRoZSBwcm90ZW9tZSBhdmVyYWdlIGFuZCB0aGUgZGlzdHJpYnV0aW9uIG9mIFMvVCBpbiBpbmRpdmlkdWFsIHByb3RlaW5zIGFjcm9zcyB0aGUgcHJvdGVvbWUuCmBgYHtyfQp0bXAuZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gIm91dHB1dC9TVC1mcmVxLyIsIHBhdHRlcm4gPSAiKlNULWZyZXEudHN2Lmd6IikKZmlsZXMgPC0gZmlsZS5wYXRoKCJvdXRwdXQvU1QtZnJlcSIsIHRtcC5maWxlcykKbmFtZXMoZmlsZXMpIDwtIGdzdWIoIi0iLCAiICIsIHRtcC5maWxlcykgJT4lIHdvcmQoMSwgMSkKYmdTVC5mcmVxIDwtIGZpbGVzICU+JSAKICBtYXAofnJlYWRfdHN2KC4sIGNvbF90eXBlcyA9IGNvbHMoKSkpICU+JSAKICBiaW5kX3Jvd3MoLmlkID0gIlNwZWNpZXMiKSAlPiUgCiAgbXV0YXRlKGZyZXFTID0gU2VyL2xlbmd0aCwgZnJlcVQgPSBUaHIvbGVuZ3RoLAogICAgICAgICBTZXIgPSBOVUxMLCBUaHIgPSBOVUxMLAogICAgICAgICBTcGVjaWVzID0gcGFzdGUwKHN0cl9zdWIoU3BlY2llcywgMSwgMSksICIuICIsIHN0cl9zdWIoU3BlY2llcywgMikpKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBzdGFydHNfd2l0aCgiZnJlcSIpLCBuYW1lc190byA9ICJSZXNpZHVlIiwgbmFtZXNfcHJlZml4ID0gImZyZXEiLCB2YWx1ZXNfdG8gPSAiZnJlcXVlbmN5IikKYGBgCgpJcyB0aGVyZSBhbnkgY29ycmVsYXRpb25zIGJldHdlZW4gcHJvdGVpbiBsZW5ndGggYW5kIFMvVCBmcmVxdWVuY3k/CmBgYHtyfQpiZ1NULmZyZXEgJT4lIAogIGdncGxvdChhZXMoeCA9IGxlbmd0aCwgeSA9IGZyZXF1ZW5jeSkpICsKICBnZW9tX2hleCgpICsgZmFjZXRfZ3JpZChSZXNpZHVlIH4gU3BlY2llcywgc2NhbGVzID0gImZyZWVfeSIpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgeGxhYigiUHJvdGVpbiBsZW5ndGgiKQpgYGAKRG9lc24ndCBzZWVtIHRvIGJlIHNpZ25pZmljYW50LgoKTm93IGxldCdzIGNvbWJpbmUgdGhlIEhpbCBwcm90ZWluIFMvVCBmcmVxdWVuY2VzIGFzIGFuIGV4dHJhICJzcGVjaWVzIiBpbnRvIHRoZSBgYmdTVC5mcmVxYApgYGB7cn0KdG1wIDwtIFNULmZ1bGwgJT4lIAogIG11dGF0ZShTcGVjaWVzID0gIkhpbF9mdWxsIiwgUyA9IFNlci9sZW5ndGgsIGBUYCA9IFRoci9sZW5ndGgpICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGMoUywgYFRgKSwgbmFtZXNfdG8gPSAiUmVzaWR1ZSIsIHZhbHVlc190byA9ICJmcmVxdWVuY3kiKSAlPiUgCiAgc2VsZWN0KFNwZWNpZXMsIElELCBsZW5ndGgsIFJlc2lkdWUsIGZyZXF1ZW5jeSkgJT4lIAogIGJpbmRfcm93cyhmaWx0ZXIoYmdTVC5mcmVxLCBTcGVjaWVzICE9ICJTLiBjZXJldmlzaWFlIikpCiN0bXAgPC0gU1Qubm9OVEQgJT4lIAojICBtdXRhdGUoU3BlY2llcyA9ICJIaWwtUEYxMTc2NSIsIFMgPSBTZXIvbGVuZ3RoLCBgVGAgPSBUaHIvbGVuZ3RoKSAlPiUgCiMgIHBpdm90X2xvbmdlcihjb2xzID0gYyhTLCBgVGApLCBuYW1lc190byA9ICJSZXNpZHVlIiwgdmFsdWVzX3RvID0gImZyZXF1ZW5jeSIpICU+JSAKIyAgc2VsZWN0KFNwZWNpZXMsIElELCBsZW5ndGgsIFJlc2lkdWUsIGZyZXF1ZW5jeSkgJT4lCiMgIGJpbmRfcm93cyh0bXApCgpwIDwtIHRtcCAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gU3BlY2llcywgeSA9IGZyZXF1ZW5jeSwgZmlsbCA9IFJlc2lkdWUpKSArIAogIGdlb21fYm94cGxvdChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSksIG91dGxpZXIuc2l6ZSA9IDAuMiwgb3V0bGllci5hbHBoYSA9IDAuNSkgKyAKICAjc3RhdF9zdW1tYXJ5KAogICMgIGZ1bi5kYXRhID0gIm1lYW5fc2RsIiwgIGZ1bi5hcmdzID0gbGlzdChtdWx0ID0gMSksIAogICMgIGdlb20gPSAicG9pbnRyYW5nZSIsIGNvbG9yID0gImJsYWNrIiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgwLjkpCiAgIykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlQiID0gInNreWJsdWUzIiwgIlMiID0gImxpZ2h0Ymx1ZTEiKSkgKwogICNzY2FsZV9maWxsX2JyZXdlcigpICsKICB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQoKcCMgKyBwMSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJTIiA9IGFscGhhKCJncmF5MjAiLCAwLjUpLCAiVCIgPSBhbHBoYSgiZ3JheTIwIiwgMC41KSkpCmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIxMTEyMS1IaWwtU1QtZnJlcS1jb21wYXJlZC10by1wcm90ZW9tZS5wbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDIuNSkKYGBgCgoKYGBge3J9CmJnU1QuZnJlcSAlPiUgCiAgZ3JvdXBfYnkoU3BlY2llcywgUmVzaWR1ZSkgJT4lIAogIHN1bW1hcml6ZShtZWFuID0gcm91bmQobWVhbihmcmVxdWVuY3kpLDMpKSAlPiUgCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFJlc2lkdWUsIHZhbHVlc19mcm9tID0gbWVhbikKYGBgCgojIyMgU2xpZGluZyB3aW5kb3cgZXN0aW1hdGVzClRvIGRldGVybWluZSB0aGUgUy9UIGZyZXF1ZW5jeSBpbiB0aGUgWFBfMDI4ODg5MDMzIGhvbW9sb2dzLCBJIHJhbiB0aGUgcHJvZ3JhbSBgZnJlYWtgIGZyb20gdGhlIEVNQk9TUyBzdWl0ZSB3aXRoIHRoZSBwYXJhbWV0ZXJzIG9mIDEwMCBhYSBzbGlkaW5nIHdpbmRvdyBhbmQgYSBzdGVwIHNpemUgb2YgMTAgYWEuIEFmdGVyIHJlZm9ybWF0aW5nIHRoZSBvdXRwdXQsIHRoZSByZXN0IG9mIHRoZSBhbmFseXNpcyBpcyBhY2NvbXBsaXNoZWQgYmVsb3cuCgoKYGBge3IgU19UX2ZyZXF9CiMgbG9hZCBkYXRhClNULmZyZXEgPC0gcmVhZF90c3YoIm91dHB1dC9TVC1mcmVxL1NUX2ZyZXFfMTAwXzEwX2ZyZWFrLm91dC5neiIsIGNvbF90eXBlcyA9ICJjaWQiKQpTLmZyZXEgPC0gcmVhZF90c3YoIm91dHB1dC9TVC1mcmVxL1NfZnJlcV8xMDBfMTBfZnJlYWsub3V0Lmd6IiwgY29sX3R5cGVzID0gImNpZCIpClQuZnJlcSA8LSByZWFkX3Rzdigib3V0cHV0L1NULWZyZXEvVF9mcmVxXzEwMF8xMF9mcmVhay5vdXQuZ3oiLCBjb2xfdHlwZXMgPSAiY2lkIikKIyBjb252ZXJ0IHNlcXVlbmNlIG5hbWUgY29sdW1uIHRvIGFuIG9yZGVyZWQgbGlzdCBzb3J0ZWQgYmFzZWQgb24gdGhlIGdlbmUgdHJlZSBzZXF1ZW5jZQpTVC5mcmVxIDwtIFNULmZyZXEgJT4lICBtdXRhdGUoaWQgPSBvcmRlcmVkKGlkLCBsZXZlbHMgPSByZXYoZ2VuZXRyZWVPcmRlcikpKSAjIHRoaXMgd2lsbCBwcm9kdWNlIHRoZSBkZXNpcmVkIG9yZGVyClMuZnJlcSA8LSBTLmZyZXEgJT4lICBtdXRhdGUoaWQgPSBvcmRlcmVkKGlkLCBsZXZlbHMgPSByZXYoZ2VuZXRyZWVPcmRlcikpKSAjIHRoaXMgd2lsbCBwcm9kdWNlIHRoZSBkZXNpcmVkIG9yZGVyClQuZnJlcSA8LSBULmZyZXEgJT4lICBtdXRhdGUoaWQgPSBvcmRlcmVkKGlkLCBsZXZlbHMgPSByZXYoZ2VuZXRyZWVPcmRlcikpKSAjIHRoaXMgd2lsbCBwcm9kdWNlIHRoZSBkZXNpcmVkIG9yZGVyCmBgYAoKYGBge3IgcGxvdF9TVF9mcmVxLCBmaWcud2lkdGggPSA2LCBmaWcuaGVpZ2h0PTZ9CmdncGxvdChTVC5mcmVxLCBhZXMoeCA9IGlkLCB5ID0gcG9zKSkgKyAgZ2VvbV90aWxlKGFlcyhmaWxsID0gZnJlcSkpICsKICBjb29yZF9mbGlwKCkgKyB0aGVtZV9jbGFzc2ljKCkgKyBzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlJkR3kiLCBsaW1pdHMgPSBjKDAsIDAuOCksIG9vYiA9IHNjYWxlczo6c3F1aXNoLCBicmVha3MgPSBzZXEoMCwgMC42LCBieSA9IDAuMikpICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNSksCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfYmxhbmsoKSwjIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuODUsMC4zNSksCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gYWxwaGEoImxpZ2h0Ymx1ZSIsMC41KSkpICsKICB5bGltKDEsIDQ1MDApICsgbGFicyh5ID0gIlBvc2l0aW9uIGluIHNlcXVlbmNlIiwgeCA9ICJTZXF1ZW5jZXMiLCBjb2xvciA9ICJGcmVxdWVuY3kiKSArIAogIGdndGl0bGUoIlNlcmluZS9UaHJlb25pbmUgZnJlcXVlbmN5IGluIDEwMCBhYSBzbGlkaW5nIHdpbmRvd3MiKQpnZ3NhdmUoIm91dHB1dC9pbWcvMjAyMDEyMjMtaG9tb2xvZ3MtU1QtZnJlcS0xMDBhYS13aW5kb3cucG5nIiwgYmcgPSAidHJhbnNwYXJlbnQiLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcpCmBgYApgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9Ny41fQpTVC5jb21iIDwtIGJpbmRfcm93cyhTZXIgPSBTLmZyZXEsIFRociA9IFQuZnJlcSwgLmlkID0gIlZhciIpICU+JSAKICBtdXRhdGUoVmFyID0gb3JkZXJlZChWYXIsIGxldmVscyA9IGMoIlNlciIsIlRociIpKSkKcC5zdCA8LSBnZ3Bsb3QoU1QuY29tYiwgYWVzKHggPSBpZCwgeSA9IHBvcykpICsgZ2VvbV90aWxlKGFlcyhmaWxsID0gZnJlcSksIHNpemUgPSAyKSArIAogIGZhY2V0X3dyYXAoflZhciwgc2NhbGVzID0gImZpeGVkIikgKyB0aGVtZV9jbGFzc2ljKCkgKwogIGNvb3JkX2ZsaXAoKSArIAogICNzY2FsZV9maWxsX2Rpc3RpbGxlcihwYWxldHRlID0gIlJkR3kiLCBsaW1pdHMgPSBjKC0wLjEsIDAuNzUpLCBvb2IgPSBzY2FsZXM6OnNxdWlzaCwgYnJlYWtzID0gc2VxKDAsMC43LGJ5PTAuMSkpICsKICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3cgPSAiZ3JheTEwIiwgaGlnaCA9ICIjMDA2MDAwIiwgbWlkID0gImdyYXk5MCIsIG1pZHBvaW50ID0gMC4wNSwgYnJlYWtzID0gc2VxKDAsMC43LGJ5PTAuMSkpICsKICAjc2NhbGVfZmlsbF9zdGVwczIobG93ID0gIiMwMDAwMDAiLCBoaWdoID0gIiNiMzAwMDAiLCBtaWRwb2ludCA9IDAuMSwgYnJlYWtzID0gc2VxKDAsMC43LGJ5PTAuMSkpICsKICAjc2NhbGVfZmlsbF92aXJpZGlzX2Iobi5icmVha3MgPSAxMCwgYmVnaW4gPSAwLjEpICsKICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gNCksIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGF4aXMubGluZS54ID0gZWxlbWVudF9ibGFuaygpLCMgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjkyLDAuNDApLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IGFscGhhKCJsaWdodGJsdWUiLDAuNSkpKSArCiAgeWxpbSgxLCA0NTAwKSArIHlsYWIoIlBvc2l0aW9uIGluIHNlcXVlbmNlIikgKyB4bGFiKCJTZXF1ZW5jZXMiKSAKI3Bsb3RfZ3JpZChwLmd0cmVlLCBwLnN0LCBuY29sID0gMiwgYWxpZ24gPSAndicsIHJlbF93aWR0aHMgPSBjKDEsMyksIHNjYWxlID0gYygwLjk5LDEpKQpwLnN0Cmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIwMTIyMy1TVC1mcmVxLWNvbXBvc2l0ZS5wbmciLCB3aWR0aCA9IDcsIGhlaWdodCA9IDcuNSkKYGBgCgoKIyMgVEFOR08gcHJlZGljdGlvbiBvZiDOsi1hZ2dyZWdhdGlvbiBwcm9uZSBzZXF1ZW5jZXMKClRoZSBhbXlsb2lkLWxpa2UgJFxiZXRhJC1hZ2dyZWdhdGlvbiBwcm9uZSBzZXF1ZW5jZXMgaGF2ZSB0aGUgYWJpbGl0eSB0byBtZWRpYXRlIHNlbGYtYWdncmVnYXRpb24sIHdoaWNoIGJvb3N0cyB0aGUgbG9jYWwgY29uY2VudHJhdGlvbiBvZiB0aGUgYWRoZXNpbiBtb2xlY3VsZXMgb24gdGhlIGNlbGwtc3VyZmFjZS4gU2ltaWxhciB0byB0aGUgUy9UIGZyZXF1ZW5jeSBhYm92ZSwgd2Ugd291bGQgbGlrZSB0byB1c2UgdGhlIG91dHB1dCBmcm9tIHRoZSBwcmVkaWN0aW9uIGFsZ29yaXRobSwgVEFOR08sIHRvIHZpc3VsaXplIHRoZSBkaXN0cmlidXRpb24gb2Ygc3VjaCBzZXF1ZW5jZSBtb3RpZnMgYWxvbmcgdGhlIGxlbmd0aCBvZiB0aGUgWFBfMDI4ODg5MDMzIGhvbW9sb2cgc2VxdWVuY2VzLgoKVEFOR08gcHJlZGljdGlvbnMgYW5kIHRoZSBzdWJzZXF1ZW50IGV4dHJhY3Rpb24gb2YgJFxiZXRhJC1hZ2dyZWdhdGlvbiBwcm9uZSBzZXF1ZW5jZXMgd2VyZSBkb2N1bWVudGVkIGluIHNlcGFyYXRlIFB5dGhvbiBzY3JpcHQsIFJFQURNRSBmaWxlcyBhbmQgdGhlIGB0YW5nby5SbWRgLgpgYGB7ciB0YW5nb19zZXF1ZW5jZXN9CiMgaGVyZSB3ZSBvbmx5IGxvb2sgYXQgdGhlIGV4dHJhY3RlZCB0YW5nbyBzZXF1ZW5jZXMKdGFuZ28gPC0gcmVhZF90c3YoImRhdGEvdGFuZ29fc3VtbWFyeV90YWJsZS50c3YuZ3oiLCBjb2xfdHlwZXMgPSAiY2NpaWlkZGRpY2MiKQojIHJlb3JkZXIgdGhlIHNlcXVlbmNlcyBmb3IgcGxvdHRpbmcKdGFuZ28kbmFtZSA8LSBvcmRlcmVkKHRhbmdvJG5hbWUsIGxldmVscyA9IHJldihnZW5ldHJlZU9yZGVyKSkKIyBwbG90CnAxIDwtIGdncGxvdChzZXFMZW4sIGFlcyh4ID0gbmFtZSwgeSA9IHN0YXJ0LCB4ZW5kID0gbmFtZSwgeWVuZCA9IGVuZCkpICsgZ2VvbV9zZWdtZW50KGNvbG9yID0gImdyYXk4MCIsIHNpemUgPSAyKQpwMiA8LSBnZW9tX3NlZ21lbnQoZGF0YSA9IGZpbHRlcihmZWF0dXJlLCB0eXBlID09ICJIeXBoYWxfcmVnX0NXUCIpLCBhZXMoeCA9IG5hbWUsIHkgPSBzdGFydCwgeGVuZCA9IG5hbWUsIHllbmQgPSBlbmQpLCBzaXplID0gMiwgY29sb3IgPSAiIzNkODRjNiIpCnAzIDwtIGdlb21fc2VnbWVudChkYXRhID0gdGFuZ28sIGFlcyh4ID0gbmFtZSwgeGVuZCA9IG5hbWUsICB5ID0gaWZlbHNlKHN0YXJ0LTQgPj0gMCwgc3RhcnQtNCwgMCksIHllbmQgPSBlbmQgKyA0LCBjb2xvciA9IG1lZGlhbiksIHNpemUgPSAyKQpwNCA8LSBwMSArIHAyICsgcDMgKyBjb29yZF9mbGlwKCkgKyB0aGVtZV9jbGFzc2ljKCkgKyAKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MobGltaXRzID0gYyg1LDEwMSksIGJyZWFrcyA9IGMoNSxzZXEoMjUsMTAwLDI1KSksIGRpcmVjdGlvbiA9IC0xKSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDQpLCBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lLnkgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLmxpbmUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC43NSwwLjM1KSwKICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgICAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IGFscGhhKCJsaWdodGJsdWUiLDAuMSkpKSArCiAgeWxpbSgtMiwgNDUwMCkgKyBsYWJzKHkgPSAiUG9zaXRpb24gaW4gc2VxdWVuY2UiLCB4ID0gIlNlcXVlbmNlcyIsIGNvbG9yID0gIlRBTkdPIHNjb3JlIikgKyAKICBnZ3RpdGxlKCJUQU5HTyBoaXRzIHdpdGggSHlwaGFsX3JlZ19DV1AgZG9tYWluIG1hc2tlZCIpCnA0CiNwbG90X2dyaWQocC5ndHJlZSwgcDQsIG5jb2wgPSAyLCBhbGlnbiA9ICd2JywgcmVsX3dpZHRocyA9IGMoMSwyLjUpLCBzY2FsZSA9IGMoMS4wMSwxKSkKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjEwMTEwLXRhbmdvLXNjb3JlLXNlZ21lbnQucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSA3LjUpCmBgYAojIyMgTnVtYmVyIGFuZCBzcGFjaW5nIG9mIFRBTkdPIGhpdHMgaW4gZWFjaCBwcm90ZWluIHNlcXVlbmNlClNvbWUgb2YgdGhlIGZvbGxvd2luZyBhbmFseXNpcyBpcyBtb2RlbGVkIGFmdGVyIGFub3RoZXIgUiBtYXJrZG93biBbMjAyMDEwMzEtdGFuZ28uUm1kXShhbmFseXNpcy8yMDIwMTAzMS10YW5nby5SbWQpLgoKSG93IG1hbnkgVEFOR08gcHJlZGljdGVkIM6yLWFnZ3JlZ2F0aW9uIHNlcXVlbmNlcyBkb2VzIGVhY2ggaG9tb2xvZyBoYXMsIGVpdGhlciBpbiB0aGUgZW50aXJlIHByb3RlaW4gb3IgaW4gdGhlIG5vbi1OVEQgcG9ydGlvbj8gVG8gZG8gc28gSSdsbCB0YWtlIGFkdmFudGFnZSBvZiB0aGUgR1JhbmdlcyBwYWNrYWdlIHRvIGRpc3Rpbmd1aXNoIFRBTkdPIGhpdHMgdGhhdCBhcmUgd2l0aGluIG9yIG91dHNpZGUgdGhlIFBGMTE3NjUgZG9tYWluKHMpIGZvciBlYWNoIHByb3RlaW4uIEZpcnN0LCBJIG5lZWQgdG8gY29udmVydCB0aGUgZmVhdHVyZSB0YWJsZSBpbnRvIGEgR1JhbmdlcyBvYmplY3Qgd2l0aCB0aGUgc2VxdWVuY2UgbmFtZXMgdXNlZCBhcyBjaHJvbW9zb21lIG5hbWVzIGFuZCBzdGFydCBhbmQgZW5kIGFzIHRoZSBJUmFuZ2VzIHN0YXJ0IGFuZCBlbmQuIFRoZW4gSSdsbCBjb252ZXJ0IHRoZSB0YW5nbyBvdXRwdXQgYWxzbyBpbnRvIGEgR1JhbmdlcyBvYmplY3QgaW4gdGhlIHNhbWUgd2F5LCB3aXRoIHRoZSBleGNlcHRpb24gb2Yga2VlcGluZyB0aGUgbWVkaWFuIGFzIHRoZSBzY29yZSBmaWVsZC4KYGBge3J9CnBmMTE3NjUuZ3IgPC0gZmVhdHVyZSAlPiUgCiAgZmlsdGVyKHR5cGUgPT0gIkh5cGhhbF9yZWdfQ1dQIikgJT4lIAogIHNlbGVjdChzZXFuYW1lID0gbmFtZSwgc3RhcnQsIGVuZCkgJT4lIAogIG1ha2VHUmFuZ2VzRnJvbURhdGFGcmFtZShpZ25vcmUuc3RyYW5kID0gVFJVRSkKCnRhbmdvLmdyIDwtIHRhbmdvICU+JSAKICBzZWxlY3Qoc2VxbmFtZSA9IG5hbWUsIHN0YXJ0LCBlbmQsIHNjb3JlID0gbWVkaWFuKSAlPiUgCiAgbWFrZUdSYW5nZXNGcm9tRGF0YUZyYW1lKGtlZXAuZXh0cmEuY29sdW1ucyA9IFRSVUUsIGlnbm9yZS5zdHJhbmQgPSBUUlVFKQoKIyBjb3VudCB0aGUgbnVtYmVyIG9mIHRhbmdvIGhpdHMgaW4gdGhlIHBmMTE3NjUgZG9tYWluCnRhbmdvLmdyLmluIDwtIHN1YnNldEJ5T3ZlcmxhcHModGFuZ28uZ3IsIHBmMTE3NjUuZ3IpICU+JSAKICBhc190aWJibGUoKSAlPiUgCiAgbXV0YXRlKG5hbWUgPSBvcmRlcmVkKHNlcW5hbWVzLCBsZXZlbHMgPSByZXYoZ2VuZXRyZWVPcmRlcikpLCBJbk5URCA9IFRSVUUpICU+JSAKICBzZWxlY3QobmFtZSwgc3RhcnQsIGVuZCwgSW5OVEQpCgp0YW5nbzEgPC0gdGFuZ28gJT4lIAogIG11dGF0ZShJbk5URCA9IEZBTFNFKSAlPiUgCiAgcm93c191cGRhdGUodGFuZ28uZ3IuaW4sIGJ5ID0gYygibmFtZSIsICJzdGFydCIsICJlbmQiKSkKYGBgCgpPbmNlIEkgaGF2ZSB0aGUgbmV3IHRpYmJsZSBgdGFuZ28xYCB0aGF0IGNvbnRhaW5zIHRoZSBgSW5OVERgIGNvbHVtbiwgSSBjYW4gY2FsY3VsYXRlIHN1bW1hcnkgc3RhdGlzdGljczoKYGBge3J9CnRhbmdvMS5zdW0gPC0gdGFuZ28xICU+JSAKICBncm91cF9ieShuYW1lKSAlPiUgCiAgc3VtbWFyaXplKG4uYWxsID0gc3VtKCFJbk5URCksIG4uZ3QzMCA9IHN1bShyb3VuZChtZWRpYW4sMCkgPj0gMzAgJiAhSW5OVEQpKSAlPiUgCiAgbGVmdF9qb2luKHNlbGVjdChzZXFJbmZvLCBuYW1lLCBsZW5ndGgpLCBieSA9ICJuYW1lIikKCmNhdCgiQ291bnQgdGhlIHRvdGFsIG51bWJlciBvZiB0YW5nbyBoaXRzIG91dHNpZGUgdGhlIE5URDogIikKdGFibGUoY3V0KHRhbmdvMS5zdW0kbi5hbGwsIGJyZWFrcyA9IGMoMCwxLDUsMTAsSW5mKSwgcmlnaHQgPSBGQUxTRSkpCmNhdCgiQW1vbmcgdGhlIGFib3ZlIHRhbmdvIGhpdHMsIG9ubHkgY291bnQgdGhvc2Ugd2l0aCBtZWRpYW4gc2NvcmUgZ3JlYXRlciB0aGFuIDMwOiAiKQp0YWJsZShjdXQodGFuZ28xLnN1bSRuLmd0MzAsIGJyZWFrcyA9IGMoMCwxLDUsMTAsSW5mKSwgcmlnaHQgPSBGQUxTRSkpCmBgYAoKX0MuIGF1cmlzXyBIaWwxLTQgYW5kIHRoZWlyIE1EUiBvcnRob2xvZ3MgYXJlIGV4Y2VwdGlvbmFsIGluIFRBTkdPIGhpdHMgbnVtYmVyIGFuZCBkaXN0cmlidXRpb24uIFRoZXkgYXJlIG9uIGF2ZXJhZ2UgbG9uZ2VyIHRoYW4gdGhlIHJlc3Qgb2YgdGhlIGhvbW9sb2dzCgpgYGB7cn0KIyB0aGUgc3Vic2V0IG9mIGhvbW9sb2dzIGFyZSAyMi00MyBpbiB0aGUgZ2VuZXRyZWVPcmRlciB2ZWN0b3IuIHVzZSB0aGlzIHRvIGRvIHRoZSBmb2xsb3dpbmcKdGFuZ28xLnN1bSAlPiUKICBncm91cF9ieShgSGlsMS00YCA9IG5hbWUgJWluJSBnZW5ldHJlZU9yZGVyWzE4OjQzXSkgJT4lIAogIHN1bW1hcml6ZShuID0gbigpLCBtTGVuID0gbWVkaWFuKGxlbmd0aCksIG5UQU5HTyA9IG1lZGlhbihuLmFsbCksIG5UQU5HTzMwID0gbWVkaWFuKG4uZ3QzMCkpCmBgYAoKSG93IGFib3V0IHRoZSBzcGFjaW5nIGJldHdlZW4gdGhlIHJlcGxpY2F0ZXM/CmBgYHtyfQojIGZpcnN0IGNvdW50IHRoZSBudW1iZXIgb2Ygb3V0LW9mLU5URCwgbWVkaWFuIHNjb3JlID49IDMwIGhpdHMgcGVyIHNlcXVlbmNlCm1vdGlmLnBlci5zZXEgPC0gdGFuZ28xICU+JSAKICBncm91cF9ieShuYW1lKSAlPiUgCiAgc3VtbWFyaXplKG4uYWxsID0gc3VtKCFJbk5URCAmIHJvdW5kKG1lZGlhbiwgMCkgPj0gMzApKQoKIyBuZXh0IHdlIHdpbGwgZmlsdGVyIHRoZSB0YW5nbyBkYXRhc2V0IGluIG9yZGVyIHRvIHJlY2FsY3VsYXRlIHRoZSBpbnRlcnZhbHMKIyB0aGlzIHdpbGwgcmVzdWx0IGluIDE0IHNlcXVlbmNlcyB0byBiZSBkcm9wcGVkIHNpbmNlIHRoZXkgaGF2ZSAwIGhpdHMgbWVldGluZwojIHRoZSBjcml0ZXJpYS4gd2Ugd2lsbCBhZGQgdGhlbSBiYWNrIGJ5IHJpZ2h0X2pvaW4oKSB3aXRoIHRoZSB0aWJibGUgYWJvdmUKbW90aWYucGVyLnNlcSA8LSB0YW5nbzEgJT4lIAogICMgbGltaXQgdG8gc2VxdWVuY2VzIG5vdCBpbiB0aGUgUEYxMTc2NSBkb21haW4gYW5kIHdpdGggbWVkaWFuIHNjb3JlID49IDMwCiAgZmlsdGVyKCFJbk5URCwgcm91bmQobWVkaWFuLDApID49IDMwKSAlPiUgCiAgZ3JvdXBfYnkobmFtZSkgJT4lIAogICMgcmVjYWxjdWxhdGUgdGhlIGludGVydmFsIHNpbmNlIHdlIGFyZSBub3cgbGltaXRpbmcgdGhlIGhpdHMgdG8gPj0gMzAKICBtdXRhdGUoaW50ZXJ2YWwgPSBzdGFydCAtIGxhZyhlbmQpIC0gMSkgJT4lIAogICMgc3VtbWFyaXplIHRoZSByZXN1bHRzIGF0IGEgc2VxdWVuY2UgbGV2ZWwKICBzdW1tYXJpemUobi50eXBlID0gbGVuZ3RoKHVuaXF1ZShzZXEpKSwKICAgICAgICAgICAgbi5hbGwgPSBuKCksCiAgICAgICAgICAgIG1lZFNjb3JlID0gcm91bmQobWVhbihtZWRpYW4pLDEpLAogICAgICAgICAgICBJVlQgPSByb3VuZChtZWFuKGl2dCksMiksCiAgICAgICAgICAgIGF2Zy5pbnR2ID0gcm91bmQobWVhbihpbnRlcnZhbCwgbmEucm0gPSBUKSwxKSwgCiAgICAgICAgICAgIElRUi5pbnR2ID0gcm91bmQoSVFSKGludGVydmFsLCBuYS5ybSA9IFQpLzEuMzQ5ICwxKSwKICAgICAgICAgICAgIyBtZWRpYW4gYWJzb2x1dGUgZGV2aWF0aW9uIGlzIGEgcm9idXN0IG1lYXN1cmUgb2YgdGhlIHNjYWxlIHBhcmFtZXRlcgogICAgICAgICAgICAjIGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9zdGF0cy92ZXJzaW9ucy8zLjYuMi90b3BpY3MvbWFkCiAgICAgICAgICAgIG1hZC5pbnR2ID0gcm91bmQobWFkKGludGVydmFsLCBuYS5ybSA9IFQpLDEpLAogICAgICAgICAgICBzZXFzID0gcGFzdGUodW5pcXVlKHNlcSksIGNvbGxhcHNlID0gIiwiKSwKICAgICAgICAgICAgLmdyb3VwcyA9ICJkcm9wX2xhc3QiKSAlPiUgCiAgcmlnaHRfam9pbihtb3RpZi5wZXIuc2VxLCBieSA9IGMoIm5hbWUiLCAibi5hbGwiKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuLmFsbCksIGRlc2MobWFkLmludHYpKQpEVDo6ZGF0YXRhYmxlKG1vdGlmLnBlci5zZXEpCgojIGFkZCBleHRyYSBpbmZvcm1hdGlvbiBmb3IgcGxvdHRpbmcgYmVsb3cKbW90aWYucGVyLnNlcSA8LSBtb3RpZi5wZXIuc2VxICU+JSAKICBsZWZ0X2pvaW4oc2VsZWN0KHNlcUluZm8sIHNwZWNpZXNfaWQsIHNwZWNpZXNfZ3IsIG5hbWUpLCBieSA9ICJuYW1lIikgJT4lIAogIG11dGF0ZShyZWcuc3BhY2VkID0gaWZlbHNlKG4uYWxsID4gMyAmIG1hZC5pbnR2IDwgNSwgVFJVRSwgRkFMU0UpLAogICAgICAgICBtYWQuaW50diA9IGlmZWxzZShuLmFsbCA+IDIsIG1hZC5pbnR2LCBOQSksCiAgICAgICAgIHNwZWNpZXMgPSBvcmRlcmVkKHNwZWNpZXNfaWQsIGxldmVscyA9IGxldmVscyhzcGVjaWVzX2lkKSwgbGFiZWxzID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKHN0cl9zdWIobGV2ZWxzKHNwZWNpZXNfaWQpLDEsMSksICIuICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfc3ViKGxldmVscyhzcGVjaWVzX2lkKSwgMikpCiAgICAgICAgICksCiAgICAgICAgIHNwZWNpZXMgPSByZW9yZGVyKHNwZWNpZXMsIGRlc2Moc3BlY2llcykpLAogICAgICAgICBzcGVjaWVzX2lkID0gTlVMTCkKYGBgCkV4cG9ydCB0aGUgdGFuZ28gbW90aWYgcGVyIHNlcSByZXN1bHQKYGBge3J9Cm1vdGlmLnBlci5zZXExICU+JSAKICBtdXRhdGUoaWQgPSBnc3ViKCJfW2EtekEtWl0rJCIsICIiLCBuYW1lKSwKICAgICAgICAgc3BlY2llcyA9IGdzdWIoIi4qXyhbQS1aXSkoW2Etel0rKSQiLCAiXFwxXFwuIFxcMiIsIG5hbWUpKSAlPiUgCiAgc2VsZWN0KGlkLCBzcGVjaWVzLCBzcGVjaWVzX2dyLCBuLnR5cGUsIG4uYWxsLCBhdmcuaW50diwgbWFkLmludHYsIHNlcXMpICU+JSAKICB3cml0ZV90c3YoIm91dHB1dC90YW5nby8yMDIxMDkwNC10YW5nby1zdW1tYXJ5LXRhYmxlLnRzdiIpCmBgYAoKYGBge3J9CiMgIG11dGF0ZShDbGFkZTIgPSBpZmVsc2Uoc3BlY2llcyAlaW4lIGNsYXZpc3BvcmEsICJDbGF2aXNwb3JhIiwKIyAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc3BlY2llcyAlaW4lIGNhbmRpZGEsICJDYW5kaWRhIiwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzcGVjaWVzICVpbiUgc2FjY2hhcm8sICJTYWNjaGFyb215Y2V0YWNlYWUiLCAib3RoZXIiKSkpKQojcDAgPC0gbW90aWYucGVyLnNlcSAlPiUgCiMgIGdncGxvdChhZXMoeCA9IHNwZWNpZXMpKSArIGdlb21fYmFyKCkgKyBjb29yZF9mbGlwKCkgKwojICAjIHRoYW5rcyB0byBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8xMDgzNDM4Mi9nZ3Bsb3QyLWtlZXAtdW51c2VkLWxldmVscy1iYXJwbG90CiMgIHNjYWxlX3hfZGlzY3JldGUoZHJvcCA9IEZBTFNFKSArCiMgIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBjKDAsNSwxMCwxNSksIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsgCiMgIHlsYWIoIiMgaG9tb2xvZ3MiKQoKY29sLnJlZ3NwYyA8LSBjKCJUUlVFIiA9ICIjYWY4NDAwIiwgIkZBTFNFIiA9IGFscGhhKCJncmV5MzAiLCAwLjUpKQpwMSA8LSBtb3RpZi5wZXIuc2VxICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBzcGVjaWVzLCB5ID0gbi5hbGwpKSArIAogIGdlb21faml0dGVyKGFlcyhjb2xvciA9IHJlZy5zcGFjZWQpLCBzaXplID0gMi4yLCB3aWR0aCA9IDAuMywgaGVpZ2h0ID0gMCkgKwogICNnZW9tX2RvdHBsb3QoYWVzKGZpbGwgPSByZWcuc3BhY2VkKSwgYmluYXhpcyA9ICJ5IiwgYmlud2lkdGggPSA0LCAKICAjICAgICAgICAgICAgIHN0YWNrZGlyID0gImNlbnRlciIsIHdpZHRoID0gMC41LCBiaW5wb3NpdGlvbnMgPSAiYWxsIikgKwogICNnZW9tX3RleHQoYWVzKGxhYmVsID0gaWZlbHNlKG4uYWxsID4gMiAmIG1hZC5pbnR2IDwgNSwgIioiLCAiIikpLAogICMgICAgICAgICAgY29sb3IgPSAiZ3JleTIwIikgKwogICNzY2FsZV9zaXplX21hbnVhbCh2YWx1ZXMgPSBjKDEsMikpICsgZ3VpZGVzKHNpemUgPSAibm9uZSIpICsKICAjc2NhbGVfc2hhcGVfbWFudWFsKG5hbWUgPSAiUmVndWxhcmx5IHNwYWNlZCIsIHZhbHVlcyA9IGMoMjEsMTkpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiUmVndWxhcmx5IHNwYWNlZCIsIHZhbHVlcyA9IGNvbC5yZWdzcGMpICsKICBjb29yZF9mbGlwKCkgKyB5bGFiKCIjIFRBTkdPIGhpdHMvcHJvdGVpbiIpICsKICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAicHNldWRvX2xvZyIsIGJyZWFrcyA9IGMoMDo1LDEwLDIwLDQwKSwKICAgICAgICAgICAgICAgICAgICAgbWlub3JfYnJlYWtzID0gTlVMTCkKbGVnZW5kIDwtIGdldF9sZWdlbmQocDEgKyBndWlkZXMoY29sb3IgPSBndWlkZV9sZWdlbmQobnJvdyA9IDEpLCBwY2ggPSBndWlkZV9sZWdlbmQobnJvdyA9IDEpKSArCiAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpKQogIApwMiA8LSBtb3RpZi5wZXIuc2VxICU+JSAKICBtdXRhdGUobWFkLmludHYgPSBpZmVsc2UoaXMubmEobWFkLmludHYpLCA1MDAsIG1hZC5pbnR2KSkgJT4lIAogIGdncGxvdChhZXMoeCA9IHNwZWNpZXMsIHkgPSBtYWQuaW50dikpICsgCiAgZ2VvbV9qaXR0ZXIoYWVzKGNvbG9yID0gcmVnLnNwYWNlZCksIHNpemUgPSAyLjIsIHdpZHRoID0gMC4zLCBoZWlnaHQgPSAwKSArCiAgI2dlb21fZG90cGxvdChhZXMoZmlsbCA9IHJlZy5zcGFjZWQpLCBiaW5heGlzID0gInkiLCBiaW53aWR0aCA9IDAuNSwgCiAgIyAgICAgICAgICAgICBzdGFja2RpciA9ICJjZW50ZXIiLCB3aWR0aCA9IDAuMywgYmlucG9zaXRpb25zID0gImFsbCIpICsKICAjc2NhbGVfc2hhcGVfbWFudWFsKG5hbWUgPSAiUmVndWxhcmx5IHNwYWNlZCIsIHZhbHVlcyA9IGMoMjEsIDE5KSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIlJlZ3VsYXJseSBzcGFjZWQiLCB2YWx1ZXMgPSBjb2wucmVnc3BjKSArCiAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gInBzZXVkb19sb2ciLCAKICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLDUsMjUsMTAwLDUwMCksIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsKICBjb29yZF9mbGlwKCkgKyB5bGFiKCJWYXJpYXRpb24gb2Ygc3BhY2luZyAoTUFEKSIpCgpwMyA8LSB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiYmxhY2siKSArIGJhY2tncm91bmRfZ3JpZCgpICsKICAgICAgICAgICAgICAgICAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKcHJvdyA8LSBwbG90X2dyaWQocDEgKyBwMywgcDIgKyBwMywgbmNvbCA9IDIpCgpwbG90X2dyaWQobGVnZW5kLCBwcm93LCBuY29sID0gMSwgcmVsX2hlaWdodHMgPSBjKC4xLDEpKQoKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjEwNjMwLXRhbmdvLWhpdHMtbnVtYmVyLWFuZC1pbnRlcnZhbC5wbmciLCB3aWR0aCA9NS41LCBoZWlnaHQgPSA1KQpgYGAKCiMgRXZvbHV0aW9uYXJ5IGhpc3RvcnkKIyMgU3BlY2llcyB0cmVlCkJlbG93IGlzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgc3BlY2llcyBzdHVkaWVkIGluIHRoaXMgYW5hbHlzaXMuCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpzcHMudHJlZSA8LSByZWFkLnRyZWUoZmlsZSA9ICJkYXRhLzIwMjAwNzI0LXNwZWNpZXMtdHJlZS5ud2siKQpwLnRyZWUgPC0gc3BzLnRyZWUgJT4lIAogIGFzX3RpYmJsZSgpICU+JSAKICBtdXRhdGUobGFiZWwgPSBwYXN0ZTAoc3RyX3N1YihsYWJlbCwgMSwgMSksICIuICIsIHN0cl9zdWIobGFiZWwsIDIpKSkgJT4lIAogIGxlZnRfam9pbihzcHNEYXRhLCBieSA9ICJsYWJlbCIpICU+JSAKICBhcy50cmVlZGF0YSgpICU+JSAKICBnZ3RyZWUobGFkZGVyaXplID0gRkFMU0UpICsgeGxpbSgwLDE3KSArIHNjYWxlX3lfcmV2ZXJzZSgpICsKICBnZW9tX3RpcGxhYihhZXMoY29sb3IgPSBwYXRobyksIHNpemUgPSAzLjgsIGZvbnRmYWNlID0gIml0YWxpYyIsIG9mZnNldCA9IDAuMykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsInJlZCIpLCBndWlkZSA9ICJub25lIikgKwogICNnZW9tX2hpbGlnaHQobm9kZSA9IDI3LCBmaWxsID0gIm1hZ2VudGEiLCBhbHBoYSA9IDAuMTUpICsgIyBDbGF2aXNwb3JhCiAgZ2VvbV9oaWxpZ2h0KG5vZGUgPSAyMywgZmlsbCA9ICIjN0YwMEZGIiwgYWxwaGEgPSAwLjE1KSAgKyAjIE1EUgogICNnZW9tX2hpbGlnaHQobm9kZSA9IDMzLCBmaWxsID0gImdvbGQiLCBhbHBoYSA9IDAuMjUpICsgIyBDYW5kaWRhCiAgZ2VvbV9oaWxpZ2h0KG5vZGUgPSAyOSwgZmlsbCA9ICJwaW5rIiwgYWxwaGEgPSAwLjI1KSAgICAgKyAjIGFsYmljYW5zCiAgI2dlb21faGlsaWdodChub2RlID0gMzcsIGZpbGwgPSAiZ3JleTUwIiwgYWxwaGEgPSAwLjE1KSAgKyAjIFNhY2Nocm9teWNldGFjZWFlCiAgZ2VvbV9oaWxpZ2h0KG5vZGUgPSAzMywgZmlsbCA9ICJzdGVlbGJsdWUiLCBhbHBoYSA9IDAuMTUpICAjIGdsYWJyYXRhCiAgI2dlb21fY2xhZGVsYWJlbChub2RlID0gMjIsICBsYWJlbCA9ICJDbGF2aXNwb3JhIiwgb2Zmc2V0ID0gMy43LCBjb2xvciA9ICJtYWdlbnRhIiwgCiAgIyAgICAgICAgICAgICAgICBvZmZzZXQudGV4dCA9IDAuMiwgYW5nbGUgPSAyNzAsIGhqdXN0ID0gLjUsIGV4dGVuZCA9IDAuMykgKyAjIENsYXZpc3BvcmEKcC50cmVlICsgCiAgZ2VvbV9jbGFkZWxhYmVsKG5vZGUgPSAyMywgIGxhYmVsID0gIk1EUiIsIG9mZnNldCA9IDQuNSwgY29sb3IgPSAicHVycGxlIiwgZm9udGZhY2UgPSAyLAogICAgICAgICAgICAgICAgICBvZmZzZXQudGV4dCA9IDAuMiwgYW5nbGUgPSAyNzAsIGhqdXN0ID0gLjUsIGV4dGVuZCA9IDAuMykgKyAjIE1EUgogICNnZW9tX2NsYWRlbGFiZWwobm9kZSA9IDI3LCAgbGFiZWwgPSAiQ2FuZGlkYSIsIG9mZnNldCA9IDIuNywgY29sb3IgPSAic2FsbW9uIiwgCiAgIyAgICAgICAgICAgICAgICBvZmZzZXQudGV4dCA9IDAuMiwgYW5nbGUgPSAyNzAsIGhqdXN0ID0gLjUsIGV4dGVuZCA9IDAuMykgKyAjIENhbmRpZGEKICBnZW9tX2NsYWRlbGFiZWwobm9kZSA9IDI5LCAgbGFiZWwgPSAiYWxiaWNhbnMiLCBvZmZzZXQgPSAzLjUsIGNvbG9yID0gImhvdHBpbmsyIiwgZm9udGZhY2UgPSAyLAogICAgICAgICAgICAgICAgICBvZmZzZXQudGV4dCA9IDAuMiwgYW5nbGUgPSAyNzAsIGhqdXN0ID0gLjUsIGV4dGVuZCA9IDAuMykgKyAjIGFsYmljYW5zCiAgZ2VvbV9jbGFkZWxhYmVsKG5vZGUgPSAzMywgIGxhYmVsID0gImdsYWJyYXRhIiwgb2Zmc2V0ID0gMy41LCBjb2xvciA9ICJzdGVlbGJsdWUiLCAKICAgICAgICAgICAgICAgICAgb2Zmc2V0LnRleHQgPSAwLjIsIGFuZ2xlID0gMjcwLCBoanVzdCA9IC41LCBleHRlbmQgPSAwLjMpICMgU2FjY2hyb215Y2V0YWNlYWUKYGBgCioqRmlnLiAxIFBoeWxvZ2VuZXRpYyByZWxhdGlvbnNoaXAgb2YgdGhlIHNwZWNpZXMgYW5hbHl6ZWQgaW4gdGhpcyBzdHVkeS4qKiBUaGlzIHNwZWNpZXMgdHJlZSBpcyBiYXNlZCBvbiB0aGUgTWF4aW11bSBsaWtlbGlob29kIHBoeWxvZ2VueSBmcm9tIE11w7FveiBfZXQgYWwuXyAyMDE4IChQTUlEOiAzMDU1OTM2OSkgZm9yIHRoZSBtb3N0IHBhcnQsIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiB0aGUgc3BlY2llcyBpbiB0aGUgU2FjY2hhcm9teWNldGFjZWFlIGZhbWlseSwgd2hpY2ggaXMgYmFzZWQgb24gR2FiYWxkw7NuIF9ldCBhbC5fIDIwMTYgKFBNSUQ6IDI3NDkzMTQ2KS4KCj4gTm90ZSB0aGF0IGhlIHBsYWNlbWVudCBvZiBfRGViYXJ5b215Y2VzIGhhbnNlbmlpXyBkaWZmZXJzIGJldHdlZW4gdGhlIGFib3ZlIHR3byBwdWJsaWNhdGlvbnMuIFRoZSBmb3JtZXIgcGxhY2VkIGl0IGNsb3NlciB0byB0aGUgQ2xhdmlzcG9yYSBnZW51cyB3aGlsZSB0aGUgbGF0dGVyIHBsYWNlZCBpdCBjbG9zZXIgdG8gdGhlIENhbmRpZGEgZ2VudXMuIFdlIGJhc2VkIG91cnMgb24gdGhlIGZpcnN0IHB1YmxpY2F0aW9uLiBBbm90aGVyIGxhcmdlIHNjYWxlIHBoeWxvZ2VuZXRpYyBhbmFseXNpcyAoU2hlbiBfZXQgYWwuXyAyMDE4IChQTUlEOiAzMDQxNTgzOCkpLCBhbHNvIHBsYWNlZCBfRC4gaGFuc2VuaWlfIGNsb3NlciB0byB0aGUgQ2FuZGlkYSBnZW51cy4gSG93ZXZlciwgaW4gYm90aCBzdHVkaWVzLCB3aGljaCByZXBvcnRlZCBib290c3RyYXAgc3VwcG9ydCB2YWx1ZXMgaW4gYWRkaXRpb24gdG8gdGhlIHBoeWxvZ2VueSwgdGhlIHN1cHBvcnQgZm9yIHRoZSBwYXJ0IG9mIHRoZSB0cmVlIGludm9sdmluZyBfRC4gaGFuc2VuaWlfIGlzIHJlbGF0aXZlbHkgbG93IHN1Z2dlc3RpbmcgdW5jZXJ0YWludHkgaW4gcmVzb2x2aW5nIHRoZSByZWxhdGlvbnNoaXAuIFdlIGNob3NlIHRvIHBsYWNlIF9ELiBoYW5zZW5paV8gY2xvc2VyIHRvIHRoZSBDbGF2aXNwb3JhIGdlbnVzIGJlY2F1c2UgdGhpcyBpcyBtb3JlIGNvbmdydWVudCB3aXRoIHRoZSBnZW5lIHRyZWUgZm9yIHRoZSBIaWwgZmFtaWx5IHByb3RlaW5zIHdlIGluZmVycmVkIGJlbG93LgoKU3BlY2llcyB0cmVlIGZvciB0aGUgc2lkZSBvZiBhIGZpZ3VyZQpgYGB7cn0KI3AudHJlZSA8LSBzcHMudHJlZSAlPiUgYXNfdGliYmxlKCkgJT4lIAojICBtdXRhdGUobGFiZWwgPSBwYXN0ZTAoc3RyX3N1YihsYWJlbCwgMSwgMSksICIuICIsIHN0cl9zdWIobGFiZWwsIDIpKSkgJT4lIAojICBhcy50cmVlZGF0YSgpICU+JSBnZ3RyZWUobGFkZGVyaXplID0gRkFMU0UpICsgeGxpbSgwLDE1KSArIHNjYWxlX3lfcmV2ZXJzZSgpICsKIyAgZ2VvbV90aXBsYWIoc2l6ZSA9IDUsIGFsaWduID0gVFJVRSwgbGluZXNpemUgPSAuNSwgZm9udGZhY2UgPSAiaXRhbGljIiwgb2Zmc2V0ID0gMC4xKSArCiMgIGdlb21faGlsaWdodChub2RlID0gMjIsIGZpbGwgPSAiaG90cGluayIsIGFscGhhID0gMC4yKSArICMgQ2xhdmlzcG9yYQojICBnZW9tX2hpbGlnaHQobm9kZSA9IDIzLCBmaWxsID0gInB1cnBsZSIsIGFscGhhID0gMC4yKSAgKyAjIE1EUgojICBnZW9tX2hpbGlnaHQobm9kZSA9IDI3LCBmaWxsID0gImhvdHBpbmsiLCBhbHBoYSA9IDAuMikgKyAjIENhbmRpZGEKIyAgZ2VvbV9oaWxpZ2h0KG5vZGUgPSAyOSwgZmlsbCA9ICJyZWQiLCBhbHBoYSA9IDAuMikgICAgICsgIyBhbGJpY2FucwojICBnZW9tX2hpbGlnaHQobm9kZSA9IDMxLCBmaWxsID0gImdyZXkyMCIsIGFscGhhID0gMC4yKSAgICAjIFNhY2Nocm9teWNldGFjZWFlCmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIxMDYyNC1zcGVjaWVzLXRyZWUtc2lkZS5wbmciLCBwLnRyZWUsIHdpZHRoID0gMywgaGVpZ2h0ID0gNC41KQpgYGAKCiMjIyBVcGRhdGUgMjAyMS0xMC0yMiBhZGQgbmV3IHNwZWNpZXMKVGhlIHNwZWNpZXMgdHJlZSBhYm92ZSBvbmx5IGluY2x1ZGVzIHNwZWNpZXMgd2l0aCBhdCBsZWFzdCBvbmUgaG9tb2xvZyBvZiB0aGUgSGlsIGZhbWlseSBhbmQgdGh1cyBsZWF2ZXMgb3V0IHNwZWNpZXMgdGhhdCBhcmUgdXN1YWxseSBpbmNsdWRlZCBpbiB0aGUgeWVhc3QgcGh5bG9nZW55LiBIZXJlIEkgYWRkIHNvbWUgb2YgdGhlbSBiYWNrIHRvIGFjaGlldmUgdHdvIGdvYWxzOiAxKSB0byBoZWxwIG1ha2UgdGhlIHBvaW50IHRoYXQgX0NhbmRpZGFfIHBhdGhvZ2VucyBoYXZlIGV2b2x2ZWQgaW5kZXBlbmRlbnRseSBtdWx0aXBsZSB0aW1lcywgYnkgYWRkaW5nIG5vbi1wYXRob2dlbmljIHNwZWNpZXMgYXJvdW5kIHRob3NlIHRoYXQgYXJlIHBhdGhvZ2VuaWM7IDIpIHRvIGVuaGFuY2UgdGhlIHBvaW50IHRoYXQgdGhlIEhpbCBmYW1pbHkgaGFzIGV4cGVyaWVuY2VkIGxvc3NlcyBpbiB0aGUgbm9uLXBhdGhvZ2VuaWMgc3BlY2llcy4KYGBge3IsIHdhcm5pbmc9RkFMU0V9CnNwcy50cmVlLmV4cCA8LSByZWFkLnRyZWUoZmlsZSA9ICJkYXRhLzIwMjExMDE3LWV4cGFuZGVkLXNwZWNpZXMtdHJlZS1mb3ItZmlnMS5ud2siKQpzcHNEYXRhIDwtIHJlYWRfdHN2KCJkYXRhLzIwMjExMDI0LWV4dGVuZGVkLXNwZWNpZXMtQWxzLUhpbC1ob21vbG9ncy1udW1iZXItdGFibGUudHh0IikKIyBjb3JyZWN0IG5hbWVzCnNwcy50cmVlLmV4cCA8LSBzcHMudHJlZS5leHAgJT4lIAogIGFzX3RpYmJsZSgpICU+JSAKICBtdXRhdGUobGFiZWwgPSBnc3ViKCJcXC4iLCAiXFwuICIsIGxhYmVsKSkgJT4lIAogIGxlZnRfam9pbihzcHNEYXRhLCBieSA9ICJsYWJlbCIpICU+JSAKICBhcy50cmVlZGF0YSgpCiMgcGxvdHRpbmcKb2ZmID0gNC41CnAgPC0gZ2d0cmVlKHNwcy50cmVlLmV4cCwgbGFkZGVyaXplID0gRkFMU0UpICsgeGxpbSgwLDE2KSArIHNjYWxlX3lfcmV2ZXJzZSgpICsKICBnZW9tX3RpcGxhYihhZXMoY29sb3IgPSBwYXRobyksIHNpemUgPSAzLjUsIGZvbnRmYWNlID0gIml0YWxpYyIsIG9mZnNldCA9IDAuMykgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsInJlZCIpLCBndWlkZSA9IEZBTFNFKSArCiAgI2dlb21faGlsaWdodChub2RlID0gMjcsIGZpbGwgPSAibWFnZW50YSIsIGFscGhhID0gMC4xNSkgKyAjIENsYXZpc3BvcmEKICBnZW9tX2hpbGlnaHQobm9kZSA9IDI4LCBmaWxsID0gIiM3RjAwRkYiLCBhbHBoYSA9IDAuMTUpICArICMgTURSCiAgI2dlb21faGlsaWdodChub2RlID0gMzMsIGZpbGwgPSAiZ29sZCIsIGFscGhhID0gMC4yNSkgKyAjIENhbmRpZGEKICBnZW9tX2hpbGlnaHQobm9kZSA9IDM1LCBmaWxsID0gInBpbmsiLCBhbHBoYSA9IDAuMjUpICAgICArICMgYWxiaWNhbnMKICAjZ2VvbV9oaWxpZ2h0KG5vZGUgPSAzNywgZmlsbCA9ICJncmV5NTAiLCBhbHBoYSA9IDAuMTUpICArICMgU2FjY2hyb215Y2V0YWNlYWUKICBnZW9tX2hpbGlnaHQobm9kZSA9IDQxLCBmaWxsID0gInN0ZWVsYmx1ZSIsIGFscGhhID0gMC4xNSkgICsgIyBnbGFicmF0YQogICNnZW9tX2NsYWRlbGFiZWwobm9kZSA9IDI3LCAgbGFiZWwgPSAiQ2xhdmlzcG9yYSIsIG9mZnNldCA9IG9mZisxLjIsIGNvbG9yID0gIm9yY2hpZDMiLCAKICAjICAgICAgICAgICAgICAgIG9mZnNldC50ZXh0ID0gMC4zLCBhbmdsZSA9IDI3MCwgaGp1c3QgPSAuNSwgZXh0ZW5kID0gMC4zKSArICMgQ2xhdmlzcG9yYQogIGdlb21fY2xhZGVsYWJlbChub2RlID0gMjgsICBsYWJlbCA9ICJNRFIiLCBvZmZzZXQgPSBvZmYrMSwgY29sb3IgPSAiIzdGMDBGRiIsIGZvbnRmYWNlID0gMiwKICAgICAgICAgICAgICAgICAgb2Zmc2V0LnRleHQgPSAwLjMsIGFuZ2xlID0gMjcwLCBoanVzdCA9IC41LCBleHRlbmQgPSAwLjMpICsgIyBNRFIKICBnZW9tX2NsYWRlbGFiZWwobm9kZSA9IDMzLCAgbGFiZWwgPSAiQ2FuZGlkYSIsIG9mZnNldCA9IG9mZiwgY29sb3IgPSAib3JhbmdlMiIsIGJhcnNpemUgPSAwLjIsIAogICAgICAgICAgICAgICAgICBvZmZzZXQudGV4dCA9IDAuMywgYW5nbGUgPSAyNzAsIGhqdXN0ID0gLjUsIGV4dGVuZCA9IDAuMykgKyAjIENhbmRpZGEKICBnZW9tX2NsYWRlbGFiZWwobm9kZSA9IDM1LCAgbGFiZWwgPSAiYWxiaWNhbnMiLCBvZmZzZXQgPSBvZmYrMSwgY29sb3IgPSAiaG90cGluazIiLCBmb250ZmFjZSA9IDIsCiAgICAgICAgICAgICAgICAgIG9mZnNldC50ZXh0ID0gMC4zLCBhbmdsZSA9IDI3MCwgaGp1c3QgPSAuNSwgZXh0ZW5kID0gMC4zKSArICMgYWxiaWNhbnMKICBnZW9tX2NsYWRlbGFiZWwobm9kZSA9IDM3LCAgbGFiZWwgPSAiU2FjY2hhcm9teWNldGFjZWFlIiwgb2Zmc2V0ID0gb2ZmLCBjb2xvciA9ICJncmV5NTAiLAogICAgICAgICAgICAgICAgICBiYXJzaXplID0gMC4yLAogICAgICAgICAgICAgICAgICBvZmZzZXQudGV4dCA9IDAuMywgYW5nbGUgPSAyNzAsIGhqdXN0ID0gLjUsIGV4dGVuZCA9IDAuMykgKyAjIFNhY2Nocm9teWNldGFjZWFlCiAgZ2VvbV9jbGFkZWxhYmVsKG5vZGUgPSA0MSwgIGxhYmVsID0gImdsYWJyYXRhIiwgb2Zmc2V0ID0gb2ZmKzEsIGNvbG9yID0gInN0ZWVsYmx1ZSIsIGZvbnRmYWNlID0gMiwKICAgICAgICAgICAgICAgICAgb2Zmc2V0LnRleHQgPSAwLjMsIGFuZ2xlID0gMjcwLCBoanVzdCA9IC41LCBleHRlbmQgPSAwLjMpICMgZ2xhYnJhdGEKcApnZ3NhdmUoIm91dHB1dC9pbWcvMjAyMTEwMjMtc3BlY2llcy10cmVlLW11bHRpcGxlLW9yaWdpbi5wbmciLCB3aWR0aCA9IDUsIGhlaWdodCA9IDUpCmBgYApBbHNvIHZpc3VhbGl6ZSB0aGUgbnVtYmVyIG9mIGhvbW9sb2dzIGluIHRoZSBBbHMgYW5kIEhpbCBmYW1pbGllcwpgYGB7cn0KZGYwIDwtIHNlbGVjdChzcHNEYXRhLCBsYWJlbCwgQWxzLCBIaWwpICU+JSAgCiAgcGl2b3RfbG9uZ2VyKEhpbDpBbHMsIG5hbWVzX3RvID0gImZhbWlseSIsIHZhbHVlc190byA9ICJudW1iZXIiKSAlPiUgCiAgbXV0YXRlKHNwZWNpZXMgPSBmYWN0b3IobGFiZWwsIGxldmVscyA9IHJldihzcHNEYXRhJGxhYmVsKSkpCnAgPC0gZ2dwbG90KGRmMCwgYWVzKHggPSBmYW1pbHksIHkgPSBzcGVjaWVzKSkgKyAKICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBudW1iZXIpLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC40KSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG51bWJlciksIGNvbG9yID0gImJsYWNrIikgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGUgPSAiR3JleXMiLGRpcmVjdGlvbiA9IDEpICsKICBzY2FsZV94X2Rpc2NyZXRlKHBvc2l0aW9uID0gInRvcCIpICsKICB0aGVtZV9jb3dwbG90KCkgKyB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCnAKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjExMDI0LWV4dGVuZGVkLXNwZWNpZXMtQWxzLUhpbC1mYW1pbHktc2l6ZS1wbG90LnBuZyIsIHdpZHRoID0gMywgaGVpZ2h0ID0gNSkKYGBgCgojIyBHZW5lIHRyZWUKIyMjIFJBeE1MIHRyZWUgYmVmb3JlIHJlY29uY2lsaWF0aW9uCmBgYHtyfQpnZW5lLnRyZWUgPC0gcmVhZC5yYXhtbCgiZGF0YS9SQXhNTF9iaXBhcnRpdGlvbnNCcmFuY2hMYWJlbHMuY2x1c3RhbG9fMzcwMTI1MCIpICU+JSAKICByb290KG5vZGUgPSAxNDMsIHJlc29sdmUucm9vdCA9IFRSVUUsIGVkZ2VsYWJlbCA9IFRSVUUpCmdlbmVOYW1lQ29udmVydCA8LSByZWFkX3RzdigiZGF0YS8yMDIxMTEyNC1jYXVyaXMtSGlsLWdlbmUtbmFtZS1jb252ZXJ0LnR4dCIsIGNvbF90eXBlcyA9ICJjYyIpCmRmMCA8LSBzZXFJbmZvICU+JSBzZWxlY3QobmFtZSwgc3BlY2llc19pZCwgc3BlY2llc19ncikgJT4lIGxlZnRfam9pbihnZW5lTmFtZUNvbnZlcnQsIGJ5ID0gIm5hbWUiKQpnZW5lLnRyZWUgPC0gZnVsbF9qb2luKGdlbmUudHJlZSwgZGYwLCBieSA9ICJsYWJlbCIpCnNwcy5jb2xvciA8LSBjKCJNRFIgY2xhZGUiICAgPSAiIzdGMDBGRjlGIiwKICAgICAgICAgICAgICAgIkMuIGx1c2l0YW5pYWUiID0gInBsdW0yIiwgCiAgICAgICAgICAgICAgICJELiBoYW5zZW5paSIgPSAiZ3JheTUwIiwKICAgICAgICAgICAgICAgIkMuIHBhcmFwc2lsb3NpcyIgPSAib3JhbmdlMiIsCiAgICAgICAgICAgICAgICJTLiBzdGlwaXRpcyIgPSAic2xhdGVncmF5IiwKICAgICAgICAgICAgICAgImFsYmljYW5zIGNsYWRlIiA9ICJob3RwaW5rIiwgCiAgICAgICAgICAgICAgICJTYWNjaGFyb215Y2V0YWNlYWUiID0gImdyZXkyMCIpCnAgPC0gZ2d0cmVlKGdlbmUudHJlZSwgYWVzKGFscGhhID0gYm9vdHN0cmFwKSwgbGFkZGVyaXplID0gVFJVRSkgKwogIGdlb21fbm9kZWxhYihhZXMoeCA9IGJyYW5jaCwgbGFiZWwgPSBib290c3RyYXAsIHN1YnNldCA9IGJvb3RzdHJhcCA8IDgwKSwgCiAgICAgICAgICAgICAgIGFscGhhID0gMSwgdmp1c3QgPSAtLjUsIHNpemUgPSAyKSArCiAgc2NhbGVfYWxwaGFfY29udGludW91cyhuYW1lID0gIlJhcGlkIGJvb3RzdHJhcCIsIGJyZWFrcyA9IGMoMjAsNjAsMTAwKSkgKyAKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IHNwZWNpZXNfZ3IpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiQ2xhZGVzIC8gU3BlY2llcyIsIHZhbHVlcyA9IHNwcy5jb2xvcikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKcCA8LSBmbGlwKHAsIDEzNywxNDkpCnAKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjEwNjI0LXJheG1sLWdlbmUtdHJlZS1jb2xvci5wbmciLCB3aWR0aCA9IDcsIGhlaWdodCA9IDUpCmBgYAoqKkZpZy4gMiBSQXhNTCBpbmZlcnJlZCBnZW5lIHRyZWUgZm9yIEh5ci9JZmYtTGlrZSAoSElMKSBmYW1pbHkgbWVtYmVycyBpbiBBc2NvbXljZXRlcy4qKiBUaGUgYnJhbmNoIGxlbmd0aCBpcyBwcm9wb3J0aW9uYWwgdG8gdGhlIGluZmVycmVkIHN1YnN0aXRpb25zIHBlciBzaXRlLiBUaGUgdHJlZSBpcyBtYW51YWxseSByb290ZWQgb24gdGhlIFNhY2NoYXJvbXljZXRhY2VhZSBmYW1pbHksIHdoaWNoIGlzIHRoZSBvdXRncm91cCB0byBib3RoIHRoZSBDYW5kaWRhIGFuZCBDbGF2aXNwb3JhIGdlbmVyYSBhbmQgd2hvc2UgSGlsIGhvbW9sb2dzIGZvcm0gYSBkaXN0aW5jdCBncm91cC4gUHJvdGVpbiBzZXF1ZW5jZSBuYW1lcyBhcmUgbm90IHNob3duIGZvciBicmV2aXR5LCBidXQgYXJlIGNvbG9yIGNvZGVkIGJhc2VkIG9uIHRoZSBzcGVjaWVzIChncm91cHMpIHRoZXkgYmVsb25nIHRvLiBUaGUgY2xhZGUgY29sb3IgY29kZSBpcyBuZXN0ZWQgc3VjaCB0aGF0IGlmIGEgc2VxdWVuY2UgYmVsb25ncyB0byBib3RoIGEgc3BlY2llcyBhbmQgYSBjbGFkZSwgZS5nLiBfQy4gYXVyaXNfIGFuZCB0aGUgTURSIGNsYWRlLCB0aGUgc2VxdWVuY2Ugd2lsbCBiZSBjb2xvcmVkIGJhc2VkIG9uIHRoZSBzbWFsbGVyIHBoeWxvZ2VuZXRpYyB1bml0LCBpLmUuIF9DLiBhdXJpc18uCgojIyMgR2VuZSB0cmVlIGFmdGVyIHJlY29uY2lsaWF0aW9uCldlIHJlY29uY2lsZWQgdGhlIGdlbmUgdHJlZSB3aXRoIHRoZSBzcGVjaWVzIHRyZWUgaW4gTm90dW5nIDIuOS4gVGhlIHB1cnBvc2Ugb2YgdGhpcyBzdGVwIGlzIHRvIHJlY29uc3RydWN0IHRoZSBoaXN0b3J5IG9mIGdlbmUgZHVwbGljYXRpb25zIGFuZCBsb3NzZXMgKHRyYW5zZmVycyBkb24ndCBhcHBseSB0byB0aGlzIGNhc2UpIGFuZCB0byByZWFycmFuZ2Ugd2Vha2x5IHN1cHBvcnRlZCBwYXJ0cyBvZiB0aGUgZ2VuZSB0cmVlIHRvIHJlZHVjZSB0aGUgdG90YWwgZXZlbnQgc2NvcmUgaW4gYWNjb3JkYW5jZSB3aXRoIHRoZSBldm9sdXRpb25hcnkgcGFyc2ltb255IHByaW5jaXBhbC4KCkJyaWVmbHksIGFmdGVyIGJvdGggdGhlIGdlbmUgdHJlZSBhbmQgdGhlIHJvb3RlZCBzcGVjaWVzIHRyZWUgKHNlZSBhYm92ZSkgd2VyZSBsb2FkZWQgaW50byB0aGUgZ3JhcGhpYyB1c2VyIGludGVyZmFjZSwgdGhlIHJvb3RpbmcgYW5hbHlzaXMgd2FzIHJ1biBhbmQgdGhlIGJyYW5jaCByZWNlaXZpbmcgdGhlIGhpZ2hlc3Qgcm9vdCBzY29yZSB3YXMgdGhlIHNhbWUgYXMgdGhlIG1hbnVhbGx5IGNob3NlbiBicmFuY2ggYXMgc2hvd24gaW4gRmlnLiAyLCBuYW1lbHkgdGhlIGFuY2VzdHJhbCBicmFuY2ggZm9yIGFsbCB0aGUgU2FjY2hhcm9teWNldGFjZWFlIHByb3RlaW5zLiBBZnRlciByb290aW5nIGFuZCByZWNvbmNpbGlhdGlvbiwgYSB0b3RhbCBvZiA1OSBkdXBsaWNhdGlvbnMgYW5kIDQ3IGxvc3NlcyB3ZXJlIGluZmVycmVkLiBUaGUgcmVjb25jaWxlZCBnZW5lIHRyZWUgd2FzIHRoZW4gcmVhcnJhbmdlZC4gSW4gdGhpcyBzdGVwLCB3ZWFrbHkgc3VwcG9ydGVkIGJyYW5jaGVzIC0tIHRob3NlIHdpdGggcmFwaWQgYm9vdHN0cmFwIHNjb3JlIDwgOTAlIC0tIHdlcmUgYWxsb3dlZCB0byBiZSBzd2FwcGVkIGFuZCB0aGUgZXZlbnQgc2NvcmUsIGNhbGN1bGF0ZWQgYXMgYSB3ZWlnaHRlZCBzdW0gb2YgdGhlIHRvdGFsIGxvc3MgYW5kIGR1cGxpY2F0aW9uIGV2ZW50cywgd2FzIGNhbGN1bGF0ZWQgZm9yIGVhY2ggcmVhcnJhbmdlbWVudC4gQW1vbmcgYWxsIHJlYXJyYW5nZWQgZ2VuZSB0cmVlIHRvcG9sb2dpZXMsIHRoZSBvbmUgd2l0aCB0aGUgbG93ZXN0IGV2ZW50IHNjb3JlIGhhZCA0MiBkdXBsaWNhdGlvbnMgYW5kIDEyIGxvc3Nlcy4gVGhpcyBpcyB0aGUgZ2VuZSB0cmVlIHRoYXQgaXMgc2hvd24gYmVsb3cuCmBgYHtyfQpnZW5lLnRyZWUucmVjIDwtIHJlYWQubmh4KGZpbGUgPSAib3V0cHV0L25vdHVuZy9SQXhNTF9iaXBhcnRpdGlvbnNfY2x1c3RhbG9fMzcwMTI1MF9yZWFycmFuZ2U4MC5uaHgiKQpnZW5lLnRyZWUucmVjIDwtIGZ1bGxfam9pbihnZW5lLnRyZWUucmVjLCBkZjAsIGJ5ID0gImxhYmVsIikKZ2d0cmVlKGdlbmUudHJlZS5yZWMsIGxhZGRlcml6ZSA9IEZBTFNFLCBicmFuY2gubGVuZ3RoID0gIm5vbmUiKSArIHhsaW0oMCwyMCkgKyBzY2FsZV95X3JldmVyc2UoKSArCiAgZ2VvbV9sYWJlbChhZXMoeCA9IGJyYW5jaCwgbGFiZWwgPSAgaWZlbHNlKFMgJWluJSBjKCJTYWNjaGFyb215Y2V0YWNlYWUiLCAiQ1VHLVNlcjEiLCAiREgiLCAiU1MiKSAmIG5vZGUgIT0gMTA2LCBTLCBOQSkpLCAgZmlsbCA9ICJncmF5Iiwgc2l6ZSA9IDMpICsKICBnZW9tX3RpcGxhYihzaXplID0gMS41LCBvZmZzZXQgPSAwLjIpICsKICBnZW9tX25vZGVwb2ludChhZXMoZmlsbCA9IEQpLCBzaGFwZSA9IDIxLCBjb2xvciA9IGFscGhhKCJncmV5MTAiLCAwKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJZIiA9ICJyZWQyIiwgIk4iID0gYWxwaGEoImdyZXkxMCIsIDApKSkgKwogICNnZW9tX3RleHQoYWVzKGxhYmVsID0gaWZlbHNlKEQgPT0gIlkiLCAiRCIsIE5BKSksIGhqdXN0ID0gLTAuNCwgc2l6ZSA9IDIsIGNvbG9yID0gInJlZCIpICsKICBnZW9tX3RpcHBvaW50KGFlcyhjb2xvciA9IHNwZWNpZXNfZ3IpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAiQ2xhZGVzIiwgdmFsdWVzID0gc3BzLmNvbG9yKSArCiAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpKQpnZ3NhdmUoIm91dHB1dC9pbWcvMjAyMTExMjQtcmVjb25jaWxlZC1yZWFycmFuZ2VkODAtZ2VuZS10cmVlLWNvbG9yLnBuZyIsIHdpZHRoID0gNy41LCBoZWlnaHQgPSA3KQpgYGAKKipGaWcuIDMgUmVjb25jaWxlZCBhbmQgcmVhcnJhbmdlZCBnZW5lIHRyZWUgZm9yIEh5ci9JZmYtTGlrZSAoSElMKSBmYW1pbHkgbWVtYmVycyBpbiBBc2NvbXljZXRlcy4qKiBUaGUgY2xhZG9ncmFtIHNob3dzIG9ubHkgdGhlIHRvcG9sb2d5IG9mIHRoZSB0cmVlLCB3aXRoIGVuZHBvaW50cyBjb2xvcmVkIGluIHRoZSBzYW1lIHdheSBhcyBpbiBGaWcuIDIuIEEgcmVkICJEIiBuZXh0IHRvIGFuIGludGVybmFsIG5vZGUgaW5kaWNhdGVzIGFuIGluZmVycmVkIGdlbmUgZHVwbGljYXRpb24gZXZlbnQgYXQgdGhhdCBub2RlLiBUaGUgbGFiZWxzIHdpdGggZ3JheSBiYWNrZ3JvdW5kIGhpZ2hsaWdodCB0aGUgbWFpbiBmZWF0dXJlcyBvZiB0aGUgdHJlZTogMSkgdGhlIFNhY2NoYXJvbXljZXRhY2VhZSBzZXF1ZW5jZXMgZm9ybSB0aGUgb3V0Z3JvdXAsIHN1Z2dlc3RpbmcgdGhlcmUgd2FzIG5vIGFuY2llbnQgZHVwbGljYXRpb24gcHJpb3IgdG8gdGhlIGRpdmVyZ2VuY2Ugb2YgdGhlIGZhbWlseSBhbmQgdGhlIHJlbWFpbmluZyBzcGVjaWVzOyAyKSB0aGUgQ1VHLVNlcjEgY2xhZGUsIHdoaWNoIGNvbnRhaW5zIGJvdGggdGhlIENhbmRpZGEgYW5kIENsYXZpc3BvcmEgZ2VuZXJhLCBmb3JtcyB0d28gZHVwbGljYXRlIGdyb3Vwcywgc3VnZ2VzdGluZyBhbiBlYXJseSBkdXBsaWNhdGlvbiBldmVudCBpbiB0aGUgY2xhZGU7IDMpIHRoZSB0b3AgQ1VHLVNlcjEgYnJhbmNoIGZ1cnRoZXIgZXhwZXJpZW5jZWQgZXh0ZW5zaXZlIGR1cGxpY2F0aW9ucyBfaW5kZXBlbmRlbnRseV8gaW4gdGhlIENsYXZpc3BvcmEgZ2VudXMsIGxhYmVsZWQgYnkgdGhlIG91dGdyb3VwIF9ELiBoYW5zZW5paV8gKERIKSwgYW5kIHRoZSBDYW5kaWRhIGdlbnVzLCBsYWJlbGVkIGJ5IHRoZSBvdXRncm91cCBfUy4gc3RpcGl0aXNfIChTUykuIApgYGB7ciwgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9NX0KcC5ndHJlZSA8LSBnZ3RyZWUoZ2VuZS50cmVlLnJlYywgbGFkZGVyaXplID0gRkFMU0UsIGJyYW5jaC5sZW5ndGggPSAibm9uZSIpICsgCiAgc2NhbGVfeV9yZXZlcnNlKCkgKwogIGdlb21fbGFiZWwoYWVzKHggPSBicmFuY2gsIGxhYmVsID0gIGlmZWxzZShTICVpbiUgYygiU2FjY2hhcm9teWNldGFjZWFlIiwgIkNVRy1TZXIxIiwgIkRIIiwgIlNTIikgJiBub2RlICE9IDEwNiwgUywgTkEpKSwgIGZpbGwgPSAiZ3JheSIsIHNpemUgPSAzKSArCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3IgPSBzcGVjaWVzX2dyKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gIkNsYWRlcyIsIHZhbHVlcyA9IHNwcy5jb2xvcikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjEwNjI2LXJlY29uY2lsZWQtZ2VuZS10cmVlLXNpZGUucG5nIiwgd2lkdGggPSAzLCBoZWlnaHQgPSA2KQpgYGAKCmV4cG9ydCB0aGUgZ2VuZSB0cmVlIG9yZGVyIGZvciBwbG90dGluZyBkb21haW4gZmVhdHVyZSBtYXBzCmBgYHtyfQpnZW5lLnRyZWUucmVjICU+JSBhc190aWJibGUoKSAlPiUgcHVsbChuYW1lKSAlPiUgaGVhZCgxMDQpICU+JSAKICBjYXQoZmlsZSA9ICJkYXRhL3Jlb3JkZXJfYnlfZ2VuZV90cmVlLnR4dCIsIHNlcCA9ICJcbiIpCmBgYAoKIyMjIEdhaW5zIGFuZCBsb3NzZXMgb24gc3BlY2llcyB0cmVlCmBgYHtyfQojIHJlYWQgaW4gbm90dW5nIHBhcnNlZCBldmVudCBzdW1tYXJ5IHN0YXRzCm5vdHVuZy5zdGF0IDwtIHJlYWRfdHN2KCJvdXRwdXQvbm90dW5nL1JBeE1MX2JpcGFydGl0aW9ucy5jbHVzdGFsb18zNzAxMjUwX3JlYXJyYW5nZTgwX2V2ZW50X3N1bW1hcnkudHh0IiwgY29sX3R5cGVzID0gJ2NpaScpCnNwcy50cmVlICU+JSAKICBmdWxsX2pvaW4obm90dW5nLnN0YXQsIGJ5ID0gImxhYmVsIikgJT4lIAogIGFzX3RpYmJsZSgpICU+JSAKICBtdXRhdGUobGFiZWwgPSBwYXN0ZTAoc3RyX3N1YihsYWJlbCwgMSwgMSksICIuICIsIHN0cl9zdWIobGFiZWwsIDIpKSwKICAgICAgICAgcGF0aG8gPSBjKFQsVCxULFQsVCxGLFQsRixULFQsVCxGLEYsRixULFQsRixULHJlcChOQSwgMzUtMTgpKSkgJT4lIAogIGFzLnRyZWVkYXRhKCkgJT4lIAogIGdndHJlZShsYWRkZXJpemUgPSBGQUxTRSkgKyBzY2FsZV95X3JldmVyc2UoKSArCiAgZ2VvbV90aXBsYWIoYWVzKGNvbG9yID0gcGF0aG8pLCBzaXplID0gMy41LCBmb250ZmFjZSA9ICJpdGFsaWMiLCBvZmZzZXQgPSAwLjMpICsgeGxpbSgwLDExKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsYWNrIiwicmVkIiksIGd1aWRlID0gIm5vbmUiKSArCiAgI2dlb21fdGlwbGFiKHNpemUgPSAzLjUsIGZvbnRmYWNlID0gIml0YWxpYyIsIG9mZnNldCA9IDAuMSkgKwogICNnZW9tX2hpbGlnaHQobm9kZSA9IDIyLCBmaWxsID0gIm1hZ2VudGEiLCBhbHBoYSA9IDAuMTUpICsgIyBDbGF2aXNwb3JhCiAgZ2VvbV9oaWxpZ2h0KG5vZGUgPSAyMywgZmlsbCA9ICJwdXJwbGUiLCBhbHBoYSA9IDAuMTUpICArICMgTURSCiAgI2dlb21faGlsaWdodChub2RlID0gMjcsIGZpbGwgPSAiZ29sZCIsIGFscGhhID0gMC4xNSkgKyAjIENhbmRpZGEKICBnZW9tX2hpbGlnaHQobm9kZSA9IDI5LCBmaWxsID0gInBpbmsiLCBhbHBoYSA9IDAuMjUpICAgICArICMgYWxiaWNhbnMKICBnZW9tX2hpbGlnaHQobm9kZSA9IDMzLCBmaWxsID0gInN0ZWVsYmx1ZSIsIGFscGhhID0gMC4xNSkgICsgIyBnbGFicmF0YQogIGdlb21fdGV4dChhZXMobGFiZWwgPSBkdXBsaWNhdGlvbnMpLCBoanVzdCA9IDMsIHZqdXN0ID0gLS4zLAogICAgICAgICAgICBzaXplID0gMywgY29sb3IgPSAicmVkIiwgZm9udGZhY2UgPSAyKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHBhc3RlMCgiLyIsIGxvc3NlcykpLCBoanVzdCA9IDEuMywgdmp1c3QgPSAtLjMsCiAgICAgICAgICAgIHNpemUgPSAzLCBjb2xvciA9ICJncmV5MjAiLCBmb250ZmFjZSA9IDIpICsKICBnZW9tX2xhYmVsKGFlcyhsYWJlbCA9IGlmZWxzZShkdXBsaWNhdGlvbnMgPj0gMywgZHVwbGljYXRpb25zLCBOQSkpLCBoanVzdCA9IDIuMSwKICAgICAgICAgICAgIHZqdXN0ID0gLS4xMSwgc2l6ZSA9IDMsIGNvbG9yID0gInJlZCIsIGZpbGwgPSAieWVsbG93MSIsIGZvbnRmYWNlID0gMiwKICAgICAgICAgICAgIGxhYmVsLnNpemUgPSAwLCBsYWJlbC5wYWRkaW5nID0gdW5pdCgwLjEyLCAibGluZXMiKSkKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjEwNjIyLXNwZWNpZXMtdHJlZS13aXRoLWdhaW5zLWxvc3Nlcy5wbmciLCB3aWR0aCA9IDQuNSwgaGVpZ2h0ID0gNSkKYGBgCioqRmlnLiA0IEluZmVycmVkIGdlbmUgZHVwbGljYXRpb25zIGFuZCBsb3NzZXMgaW4gdGhlIEh5ci9JZmYtTGlrZSAoSElMKSBmYW1pbHkgaW4gQXNjb215Y2V0ZXMuKiogVGhlIGNsYWRvZ3JhbSBzaG93cyB0aGUgc3BlY2llcyByZWxhdGlvbnNoaXAgd2l0aCBzaGFkaW5ncyBhcyBpbiBGaWd1cmUgMS4gVGhlIG51bWJlcnMgb24gdG9wIG9mIGVhY2ggYnJhbmNoIGFyZSB0aGUgaW5mZXJyZWQgZHVwbGljYXRpb25zIChyZWQpIGFuZCBsb3NzZXMgKGJsYWNrKSB1c2luZyBOb3R1bmcgMi45LiBZZWxsb3cgaGlnaGxpZ2h0IGVtcGhhc2l6ZSB0aGUgYnJhbmNoZXMgdGhhdCBleHBlcmllbmNlIHRocmVlIG9yIG1vcmUgZHVwbGljYXRpb25zLgoKXyoqQ29uY2x1c2lvbnMqKl8KCjEuIFRoZSBIaWwgZmFtaWx5ICoqaW5kZXBlbmRlbnRseSoqIGV4cGFuZGVkIGluIHRoZSBDYW5kaWRhIGFuZCBDbGF2aXNwb3JhIGdlbmVyYS4gVGhlIG1vc3Qgc2lnbmlmaWNhbnQgZXhwYW5zaW9uIG9jY3VycmVkIHdpdGhpbiB0aGUgYWxiaWNhbnMgY2xhZGUgaW4gdGhlIENhbmRpZGEgZ2VudXMgYW5kIHRoZSBNRFIgY2xhZGUgaW4gdGhlIENsYXZpc3BvcmEgZ2VudXMuCjEuIE1hbnkgc3BlY2llcyBpbiB0aGUgU2FjY2hhcm9teWNldGFjZWFlIGZhbWlseSBoYXZlIG5vIGhvbW9sb2dzIGluIHRoaXMgZmFtaWx5IGJhc2VkIG9uIG91ciBibGFzdCBjcml0ZXJpYS4gVGhpcyBzdWdnZXN0cyB0aGF0IHRoZSBnZW5lIGZhbWlseSBoYXMgY29udHJhY3RlZCBhbmQgYW55IHJlbWFpbmluZyBob21vbG9ncyBhcmUgc2hvcnRlciB0aGFuIDUwMCBhbWlubyBhY2lkIGFuZCB0aHVzIGxpa2VseSB0byBub3QgcGxheSBhbiBhZGhlc2luIGZ1bmN0aW9uLgoKCgojIENocm9tb3NvbWFsIGxvY2F0aW9ucwpXaGlsZSBJIHdhcyBwZXJmb3JtaW5nIEJMQVNUIG9uIEZ1bmdpREIgdG8gaWRlbnRpZnkgaG9tb2xvZ3Mgb2Ygb3VyIHByb3RlaW4sIEkgbm90aWNlZCB0aGF0IG1hbnkgb2YgdGhlIGhpdHMgYXBwZWFyIHRvIGJlIGF0IHRoZSBiZWdpbm5pbmcgYW5kIGVuZCBvZiB0aGUgY2hyb21vc29tZXMuIFRvIHNlZSBpZiB0aGVyZSBpcyBhIHN5c3RlbWF0aWMgdHJlbmQgaW4gdGhlIGNocm9tb3NvbWFsIGxvY2F0aW9ucywgSSBjb2xsZWN0ZWQgdGhpcyBpbmZvcm1hdGlvbiBmb3IgYWxsIDk5IGhvbW9sb2dzIGluIHRoZSBsaXN0LiBOb3RlIGhvd2V2ZXIsIG5vdCBhbGwgZ2Vub21lcyBhcmUgYXNzZW1ibGVkIHRvIHRoZSBjaHJvbW9zb21hbCBsZXZlbCwgYW5kIGFzIGEgcmVzdWx0LCB0aGUgbG9jYXRpb25zIGZvciB0aGUgcHJvdGVpbnMgaW4gdGhvc2Ugc3BlY2llcy9zdHJhaW5zIHdvdWxkIGJlIHJlbGF0aXZlIHRvIGEgY29udGlnIG9yIHNjYWZmb2xkLiBNeSByYXRpb25hbGUgaXMgdGhhdCBpZiB0aGUgY29udGlnIG9yIHNjYWZmb2xkIGlzIGNsb3NlIHRvIGNob3JvbW9zb21hbCBsZW5ndGggKGFsdGhvdWdoIHRoYXQgdmFyaWVzIGJ5IGF0IGxlYXN0IGFuIG9yZGVyIG9mIG1hZ25pdHVkZSksIEkgY291bGQgYXQgbGVhc3QgbG9vayBhdCB0aGVtIHNlcGFyYXRlbHkuCgpgYGB7cn0KcCA8LSBzZXFJbmZvICU+JSAKICBtdXRhdGUoY2hyID0gcGFzdGUoc3Vic3RyKHNwZWNpZXNfaWQsMSw0KSwgY2hyYWNjdmVyLCBzZXAgPSAiXyIpLAogICAgICAgICBjaHIgPSByZW9yZGVyKGNociwgY2hyTCwgbWVhbikpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjaHIpKSArIAogIGdlb21fc2VnbWVudChhZXMoeGVuZCA9IGNociwgeSA9IDEsIHllbmQgPSBjaHJMLCBjb2xvciA9IGFzc2VtYmx5c3RhdHVzKSwgc2l6ZSA9IDIpICsKICBnZW9tX3NlZ21lbnQoYWVzKHhlbmQgPSBjaHIsIHkgPSBjaHJzdGFydCwgeWVuZCA9IGNocnN0YXJ0KzcwMDApLCBjb2xvciA9ICJyZWQiLCBzaXplID0gMykKcCArIGNvb3JkX2ZsaXAoKSArIHRoZW1lX2NsYXNzaWMoKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJncmV5NTAiLCAiZ3JleSIpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDUpLAogICAgICAgIGF4aXMubGluZS55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpY2tzLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOCwwLjIpLAogICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IGFscGhhKCJsaWdodGJsdWUiLDAuMSkpKSArCiAgbGFicyh5ID0gIlBvc2l0aW9uIiwgeCA9ICJDaHJvbW9zb21lcyAvIFNjYWZmb2xkcyIsIGNvbG9yID0gIkFzc2VtYmx5IFN0YXR1cyIpCmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIxMDIxMy1ob21vbG9ncy1jaHItbG9jLnBuZyIsIGJnID0gInRyYW5zcGFyZW50Iiwgd2lkdGggPSA1LCBoZWlnaHQgPSA1KQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0ID0gNH0KcCA8LSBzZXFJbmZvICU+JSBmaWx0ZXIoIShjaHJMIDwgMWU2ICYgYXNzZW1ibHlzdGF0dXMgPT0gIlBhcnRpYWwiKSkgJT4lIAogIG11dGF0ZShkVGlwID0gaWZlbHNlKHJlbExvYyA8IDAuNSwgcmVsTG9jLCAxLXJlbExvYykpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkVGlwKSkgKyBmYWNldF93cmFwKH5hc3NlbWJseXN0YXR1cykgKwogIHhsYWIoIkRpc3RhbmNlIGZyb20gQ2hyb21vc29tZSBvciBTY2FmZm9sZCBFbmRzIikgKyBsYWJzKGZpbGwgPSAiU3BlY2llcyBncm91cCIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDAuNSxieSA9IDAuMSkpCnAgKyBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDUpCmdnc2F2ZSgib3V0cHV0L2ltZy8yMDIxMDIxMy1kaXN0cmlidXRpb24tb24tY2hyb21vc29tZS1vci1zY2FmZm9sZHMtaGlzdG9ncmFtLWdyZXkucG5nIiwgd2lkdGggPSA1LCBoZWlnaHQgPSAzKQpwICsgZ2VvbV9oaXN0b2dyYW0oYWVzKGZpbGwgPSBzcGVjaWVzX2dyKSwgYmlud2lkdGggPSAwLjA1KSArIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFpcmVkIikKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjEwMjEzLWRpc3RyaWJ1dGlvbi1vbi1jaHJvbW9zb21lLW9yLXNjYWZmb2xkcy1oaXN0b2dyYW0ucG5nIiwgd2lkdGggPSA2LCBoZWlnaHQgPSAzKQpgYGAKCioqRGlzY3Vzc2lvbioqCgotIFZpc3VhbGx5IHRoZXJlIGFwcGVhciB0byBiZSBzb21lIGVucmljaG1lbnQgdG93YXJkcyB0aGUgY2hyb21vc29tZSBlbmRzIG92ZXJhbGwuCi0gQnJlYWtkb3duIGJ5IHNwZWNpZXMgcmV2ZWFsZWQgc3Ryb25nZXIgc2lnbmFscyBpbiB0aGUgYWxiaWNhbnMgY2xhZGUgYW5kIHRoZSBNRFIgY2xhZGUuCi0gVG8gcHJvcGVybHkgdGVzdCBmb3IgZW5yaWNobWVudCBpbiB0aGUgY2hyb21vc29tYWwgZW5kcyB3b3VsZCByZXF1aXJlIGRvY3VtZW50aW5nIHRoZSBnZW5lIGRlbnNpdHkgb24gdGhlIGFmZmVjdGVkIGNocm9tb3NvbWVzLiBJZiB0aGUgZGlmZmVyZW50IGNocm9tb3NvbWVzIGhhdmUgZHJhbWF0aWNhbGx5IGRpZmZlcmVudCBnZW5lIGRpc3RyaWJ1dGlvbnMsIHByb3BlciB0ZXN0cyBtYXkgbm90IGJlIHN0cmFpZ2h0Zm9yd2FyZCB0byBjb25zdHJ1Y3QuIEluc3RlYWQsIGEgYm9vdHN0cmFwcGluZyBhcHByb2FjaCBjb3VsZCBiZSBlYXNpZXIgdG8gY29uY2VpdmUgYW5kIGFwcGx5LgoKIyMgQXJlIG1lbWJlcnMgb2YgdGhpcyBwcm90ZWluIGZhbWlseSBlbnJpY2hlZCBpbiB0aGUgc3VidGVsb21lcmljIHJlZ2lvbnM/CkEgcmVjZW50IGxvbmctcmVhZCBzZXF1ZW5jaW5nIHN0dWR5IGZvciBfQy4gZ2xhYnJhdGFfIGFubm90YXRlZCAzMSBub3ZlbCBPUkZzLCBvZiB3aGljaCAyNCBhcmUgR1BJLUNlbGwgV2FsbCBQcm90ZWlucy4gVGhlIGF1dGhvcnMgY2l0ZWQgcHJldmlvdXMgbGl0ZXJhdHVyZSBzdXBwb3J0aW5nIHRoZSAgaW4gdGhlIHN1YnRlbG9tZXJpYyByZWdpb25zIFtYdSAyMDIwXShodHRwczovL2RvaS5vcmcvMTAuMTExMS9tbWkuMTQ0ODgpLiBUaGlzIHBsdXMgdGhlIGVtcGlyaWNhbCBvYnNlcnZhdGlvbiBhYm92ZSBzaG93aW5nIGFuIGFwcGFyZW50IGVucmljaG1lbnQgdG93YXJkcyB0aGUgY2hyb21vc29tZSBlbmRzIGxlYWQgbWUgdG8gYXNrIHdoZXRoZXIgdGhlcmUgaXMgaW5kZWVkIGEgc2lnbmlmaWNhbnQgZW5yaWNobWVudCBhbW9uZyB0aGlzIGZhbWlseSBvZiBwcm90ZWlucyBpbiB0aGUgc3VidGVsb21lcmljIHJlZ2lvbnMuCgpUbyBmb3JtYWxseSB0ZXN0IHRoaXMgaHlwb3RoZXNpcywgSSBuZWVkIHRvIGFjY291bnQgZm9yIHRoZSBiYWNrZ3JvdW5kIGdlbmUgZGVuc2l0eSBkaWZmZXJlbmNlcyBhbG9uZyB0aGUgY2hyb21vc29tZXMuIFRoZSBpZGVhIGlzIHRvIGNvbXBhcmUgdGhlIGNocm9tb3NvbWFsIHBvc2l0aW9ucyBmb3IgdGhpcyBncm91cCBvZiBwcm90ZWlucyBjb21wYXJlZCB3aXRoIHRoZSBnZW5lIGRlbnNpdGllcyBvbiB0aGUgY2hyb21vc29tZXMgdGhleSByZXNpZGUgb24uIFdlIHdpbGwgcmVzdHJpY3Qgb3VyIGFuYWx5c2lzIHRvIHRob3NlIGdlbm9tZXMgd2l0aCBhIGNocm9tb3NvbWFsIGxldmVsIGFzc2VtYmx5IHN0YXR1cyBmb3Igb2J2aW91cyByZWFzb25zLgoKPioqVXBkYXRlIDIwMjEtMDYtMjEqKiBXZSB3aWxsIGFsc28gaW5jbHVkZSBDLiBhdXJpcyBCMTEyMjEgYXMgd2Uga25vdyBpdHMgYXNzZW1ibHkgaXMgbmVhcmx5IGF0IHRoZSBjaHJvbW9zb21hbCBsZXZlbAoKYGBge3IgZGVjaWRlX3doaWNoX3NwZWNpZXNfdG9fdXNlfQp1c2Uuc3BzIDwtIHNlcUluZm8gJT4lIAogIGZpbHRlcihhc3NlbWJseXN0YXR1cyA9PSAiQ2hyb21vc29tZSIgfCBzcGVjaWVzX2lkID09ICJDYXVyaXMiKSAlPiUKICBmaWx0ZXIoIXNwZWNpZXNfaWQgJWluJSBjKCJDZHVibGluaWVuc2lzIiwgIk5jYXN0ZWxsaWkiKSkgJT4lCiAgIyBwaHlsb2dlbmV0aWMgcmVsYXRlZG5lc3MgY2FuIGluZHVjZSBjb3JyZWxhdGlvbnMsIGluY2x1ZGluZyB3aGVuIHRlc3RpbmcgZm9yIGVucmljaG1lbnQgYXQgdGhlIGNocm9tb3NvbWFsIGVuZHMKICAjIGJhc2VkIG9uIHRoZSBnZW5lIGR1cGxpY2F0aW9uIHBhdHRlcm5zLCBjb21iaW5lZCB3aXRoIFlHT0Igc3ludGVueSBiYXNlZCBvcnRob2xvZ3kgYXNzaWdubWVudHMsIHRoZSBzZXQgb2Ygc3BlY2llcwogICMgdXNlZCBoZXJlLCBhZnRlciBleGNsdWRpbmcgQ2R1YmxpbmllbnNpcywgcmVwcmVzZW50IHF1YXNpIHBoeWxvZ2VuZXRpY2FsbHkgaW5kZXBlbmRlbnQgY29udHJhc3RzLCB0aGFua3MgdG8gdGhlIAogICMgbWFueSBzcGVjaWVzLXNwZWNpZmljIGR1cGxpY2F0aW9ucwogIHNlbGVjdChzcGVjaWVzX2lkLCBhc3NlbWJseSkgJT4lIHVuaXF1ZSgpCnVzZS5zcHMKIyBub3cgbGV0J3MgYWxzbyBjcmVhdGUgYSBuZXcgdGliYmxlIGZvciBvdXIgaG9tb2xvZ3MgZnJvbSB0aGVzZSBzcGVjaWVzCmZnLmZyZXEgPC0gY2hyTG9jICU+JSAKICBmaWx0ZXIoc3BlY2llc19pZCAlaW4lIHVzZS5zcHMkc3BlY2llc19pZCkgJT4lIAogICMgcmVtb3ZlIHRoZSBvbmUgQy4gYXVyaXMgZ2VuZSBsb2NhdGVkIG9uIGFuIHVuYXNzZW1ibGVkIGZyYWdtZW50CiAgZmlsdGVyKCEoc3BlY2llc19pZCA9PSAiQ2F1cmlzIiAmIGNocl9uYW1lID09ICJzY2FmZm9sZDAwMDE1IikpICU+JSAKICAjIGNoYW5nZSB0aGUgc2NhZmZvbGQgbmFtZSB0byBjaHJvbW9zb21lIG5hbWUgKHRoZXkgY29ycmVzcG9uZCkKICBtdXRhdGUoY2hyX25hbWUgPSBnc3ViKCJzY2FmZm9sZDArIiwgIiIsIGNocl9uYW1lKSkKYGBgCgpUbyBjb25kdWN0IHRoaXMgdGVzdCwgd2UgZmlyc3QgbmVlZCB0byBwcmVwYXJlIGFuZCBjb21wdXRlIHRoZSBiYWNrZ3JvdW5kIGdlbmUgZGVuc2l0aWVzLiBUbyBkbyB0aGlzLCB3ZSB3aWxsIGdhdGhlciB0aGUgZ2Vub21lIGFzc2VtYmx5IGZpbGVzIGZvciB0aGUgc2VsZWN0ZWQgc3BlY2llcywgcmVhZCB0aGVtIGludG8gUiwgYW5kIGdlbmVyYXRlIGEgdGFibGUgdGhhdCBjb250YWlucyBvbmUgcm93IGZvciBlYWNoIGdlbmUsIHdpdGggaXRzIGdlbmUgSUQsIGNocm9tb3NvbWUgbnVtYmVyLCBjaHJvbW9zb21lIGxlbmd0aCBhbmQgdGhlIHN0YXJ0IHBvc2l0aW9uIGV4cHJlc3NlZCBhcyBhIHBlcmNlbnRhZ2UgbWVhc3VyZWQgZnJvbSB0aGUgY2hyb21vc29tZSBlbmRzLgoKYGBge3IgY29tcHV0ZV9iYWNrZ3JvdW5kX2ZyZXF9CiMgMS4gcHJlcGFyZSBmaWxlIG5hbWVzCiMgICBnZXQgYWxsIGZpbGUgbmFtZXMgdGhhdCBlbmRzIHdpdGggImZlYXR1cmVfdGFibGUudHh0Lmd6Iiwgd2hpY2ggY29udGFpbiB0aGUgZ2VuZSBhbm5vdGF0aW9uCmZlYXR1cmUuZmlsZXMgPC0gbGlzdC5maWxlcyhwYXRoID0gImRhdGEvYXNzZW1ibHktaW5mby8iLCBwYXR0ZXJuID0gIipmZWF0dXJlX3RhYmxlLnR4dC5neiQiKQpuYW1lcyhmZWF0dXJlLmZpbGVzKSA8LSBzYXBwbHkoc3RyX3NwbGl0KGZlYXR1cmUuZmlsZXMsIHBhdHRlcm4gPSAiXyIpLCBmdW5jdGlvbih4KSB7CiAgcGFzdGUoeFsxXSwgeFsyXSwgc2VwID0gIl8iKQp9KQojICAgZ2V0IGFsbCBmaWxlIG5hbWVzIHRoYXQgZW5kcyB3aXRoICJhc3NlbWJseV9yZXBvcnQudHh0Iiwgd2hpY2ggY29udGFpbiB0aGUgY2hyb21vc29tYWwgbGVuZ3RoCmFzc2VtYmx5LmZpbGVzIDwtIGxpc3QuZmlsZXMocGF0aCA9ICJkYXRhL2Fzc2VtYmx5LWluZm8vIiwgcGF0dGVybiA9ICIqYXNzZW1ibHlfcmVwb3J0LnR4dCQiKQpuYW1lcyhhc3NlbWJseS5maWxlcykgPC0gc2FwcGx5KHN0cl9zcGxpdChhc3NlbWJseS5maWxlcywgcGF0dGVybiA9ICJfIiksIGZ1bmN0aW9uKHgpIHsKICBwYXN0ZSh4WzFdLCB4WzJdLCBzZXAgPSAiXyIpCn0pCnVzZS5zcHMkRmVhdHVyZUZpbGUgPC0gZmVhdHVyZS5maWxlc1t1c2Uuc3BzJGFzc2VtYmx5XQp1c2Uuc3BzJEFzc2VtYmx5RmlsZSA8LSBhc3NlbWJseS5maWxlc1t1c2Uuc3BzJGFzc2VtYmx5XQoKIyAyLiByZWFkIGluIHRoZSBhc3NlbWJseSBpbmZvcm1hdGlvbgpmZWF0dXJlLmNvbC5uYW1lcyA8LSBjKCJmZWF0dXJlIiwiY2xhc3MiLCJhc3NlbWJseSIsImFzc2VtYmx5X3VuaXQiLCJzZXFfdHlwZSIsImNocm9tb3NvbWUiLCJnZW5vbWljX2FjY2Vzc2lvbiIsInN0YXJ0IiwiZW5kIiwic3RyYW5kIiwicHJvZHVjdF9hY2Nlc3Npb24iLCJub25fcmVkdW5kYW50X3JlZnNlcSIsInJlbGF0ZWRfYWNjZXNzaW9uIiwibmFtZSIsInN5bWJvbCIsIkdlbmVJRCIsImxvY3VzX3RhZyIsImZlYXR1cmVfaW50ZXJ2YWxfbGVuZ3RoIiwicHJvZHVjdF9sZW5ndGgiLCJhdHRyaWJ1dGVzIikKYXNzZW1ibHkuY29sLm5hbWVzIDwtIGMoImNocm9tb3NvbWUiLCJzZXFfcm9sZSIsImFzc2lnbl9tb2xlY3VsZSIsInR5cGUiLCJnYl9hY2MiLCJyZWxhdGlvbnNoaXAiLAogICAgICAgICAgICAgICAgICAgICAgICAiY2hyYWNjdmVyIiwiYXNzZW1ibHlfdW5pdCIsInNlcUwiLCJ1Y3NjX25hbWUiKQpjb21wdXRlLmJnLmZyZXEgPC0gZnVuY3Rpb24ocm93KXsKICBhc3NlbWJseS5maWxlIDwtIHJvd1siQXNzZW1ibHlGaWxlIl0KICBhc3NlbWJseSA8LSByZWFkX3RzdihwYXN0ZTAoImRhdGEvYXNzZW1ibHktaW5mby8iLGFzc2VtYmx5LmZpbGUpLCBjb21tZW50ID0gIiMiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbF9uYW1lcyA9IGFzc2VtYmx5LmNvbC5uYW1lcywgY29sX3R5cGVzID0gImNjY2NjY2NjaWMiKQogIGZlYXR1cmUuZmlsZSA8LSByb3dbIkZlYXR1cmVGaWxlIl0KICBmZWF0dXJlIDwtIHJlYWRfdHN2KHBhc3RlMCgiZGF0YS9hc3NlbWJseS1pbmZvLyIsZmVhdHVyZS5maWxlKSwgY29sX25hbWVzID0gZmVhdHVyZS5jb2wubmFtZXMsCiAgICAgICAgICAgICAgICAgICAgICBjb2xfdHlwZXMgPSAiY2NjY2NjY2lpY2NjY2NjY2NpaWMiLCBza2lwID0gMSkKICByZXMgPC0gZmVhdHVyZSAlPiUgCiAgICBmaWx0ZXIoZmVhdHVyZSA9PSAibVJOQSIpICU+JSAKICAgICMgdGhlc2UgZmVhdHVyZSB0YWJsZXMgYXJlIG9yZ2FuaXplZCBoaWVyYXJjaGljYWxseSwgd2l0aCB0aGUgdG9wIGxldmVsIGJlaW5nICJnZW5lIgogICAgIyB0aGUgbmV4dCBsZXZlbCBvbmUgb2YgIm1STkEiLCAibmNSTkEiLCAidFJOQSIgb3IgInJSTkEiLiB3ZSBvbmx5IGNvdW50IHByb3RlaW4tY29kaW5nIGdlbmVzLCBpLmUuCiAgICAjICJtUk5BIi4gdGhlIHJlYXNvbiBJIGRpZG4ndCBzZWxlY3QgdGhlICJDRFMiIGZlYXR1cmUgdHlwZSBpcyBiZWNhdXNlIGluIGEgc21hbGwgbnVtYmVyIG9mIGNhc2VzLAogICAgIyBvbmUgbVJOQSBmZWF0dXJlIGNvbnRhaW5zIG1vcmUgdGhhbiBvbmUgQ0RTIGZlYXR1cmUsIHBvc3NpYmx5IGR1ZSB0byBzcGxpY2luZyBvciBhbHRlcm5hdGl2ZSAKICAgICMgdHJhbnNsYXRpb25hbCBzdGFydCBzaXRlCiAgICBzZWxlY3QoY2hyb21vc29tZSwgc3RhcnQsIGVuZCkgJT4lIAogICAgbGVmdF9qb2luKHNlbGVjdChhc3NlbWJseSwgY2hyb21vc29tZSwgY2hyYWNjdmVyLCBzZXFMKSwgYnkgPSBjKCJjaHJvbW9zb21lIiA9ICJjaHJvbW9zb21lIikpICU+JQogICAgbXV0YXRlKHJlbExvYyA9IHJvdW5kKHN0YXJ0IC8gc2VxTCwgMykpCiAgcmV0dXJuKHJlcykKfQojIGFwcGx5IHRoZSBmdW5jdGlvbiB0byB0aGUgZ2Vub21lcywgYnV0IGxlYXZlIG91dCBDLiBhdXJpcwpiZy5mcmVxIDwtIGFwcGx5KGZpbHRlcih1c2Uuc3BzLCBzcGVjaWVzX2lkICE9ICJDYXVyaXMiKSwgTUFSR0lOID0gMSwgY29tcHV0ZS5iZy5mcmVxKQpuYW1lcyhiZy5mcmVxKSA8LSB1c2Uuc3BzICU+JSBmaWx0ZXIoc3BlY2llc19pZCAhPSAiQ2F1cmlzIikgJT4lIHB1bGwoc3BlY2llc19pZCkKYGBgCgpTZXBhcmF0ZWx5IGNhbGN1bGF0ZSB0aGUgYmFja2dyb3VuZCBmcmVxdWVuY3kgZm9yIF9DLiBhdXJpc18KYGBge3J9CmNvbXB1dGUuYmcuZnJlcS5jYXUgPC0gZnVuY3Rpb24oKXsKICBhc3NlbWJseS5jb2wubmFtZXMgPC0gYygiY2hyb21vc29tZSIsInNlcV9yb2xlIiwiYXNzaWduX21vbGVjdWxlIiwidHlwZSIsImdiX2FjYyIsInJlbGF0aW9uc2hpcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICJjaHJhY2N2ZXIiLCJhc3NlbWJseV91bml0Iiwic2VxTCIsInVjc2NfbmFtZSIpCiAgcm93ID0gdXNlLnNwcyAlPiUgZmlsdGVyKHNwZWNpZXNfaWQgPT0gIkNhdXJpcyIpICMgQy4gYXVyaXMgZW50cnkKICBhc3NlbWJseS5maWxlIDwtIHJvd1siQXNzZW1ibHlGaWxlIl0KICBhc3NlbWJseSA8LSByZWFkX3RzdihwYXN0ZTAoImRhdGEvYXNzZW1ibHktaW5mby8iLGFzc2VtYmx5LmZpbGUpLCBjb21tZW50ID0gIiMiLAogICAgICAgICAgICAgICAgICAgICAgIGNvbF9uYW1lcyA9IGFzc2VtYmx5LmNvbC5uYW1lcywgY29sX3R5cGVzID0gImNjY2NjY2NjaWMiKSAlPiUgCiAgICBmaWx0ZXIoYXMuaW50ZWdlcihzdHJfc3ViKGNocm9tb3NvbWUsIC0yLCAtMSkpIDw9IDcpCiAgZmVhdHVyZS5maWxlIDwtIHJvd1siRmVhdHVyZUZpbGUiXQogIGZlYXR1cmUgPC0gcmVhZF90c3YocGFzdGUwKCJkYXRhL2Fzc2VtYmx5LWluZm8vIixmZWF0dXJlLmZpbGUpLCBjb2xfbmFtZXMgPSBmZWF0dXJlLmNvbC5uYW1lcywKICAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9ICJjY2NjY2NjaWljY2NjY2NjY2lpYyIsIHNraXAgPSAxKQogIHJlcyA8LSBmZWF0dXJlICU+JSAKICAgIGZpbHRlcihmZWF0dXJlID09ICJtUk5BIikgJT4lIAogICAgc2VsZWN0KGNocmFjY3ZlciA9IGdlbm9taWNfYWNjZXNzaW9uLCBzdGFydCwgZW5kKSAlPiUgCiAgICByaWdodF9qb2luKHNlbGVjdChhc3NlbWJseSwgY2hyb21vc29tZSwgY2hyYWNjdmVyLCBzZXFMKSwgYnkgPSAiY2hyYWNjdmVyIikgJT4lCiAgICBtdXRhdGUocmVsTG9jID0gcm91bmQoc3RhcnQgLyBzZXFMLCAzKSwKICAgICAgICAgICBjaHJvbW9zb21lID0gZ3N1Yigic2NhZmZvbGQwKyIsIiIsY2hyb21vc29tZSkpCiAgcmV0dXJuKHJlcykKfQpiZy5mcmVxJENhdXJpcyA8LSBjb21wdXRlLmJnLmZyZXEuY2F1KCkKYGBgCgpMZXQncyB0YWtlIGEgbG9vayBhdCB0aGUgZ2VuZSBkZW5zaXR5IGluIG9uZSBvZiB0aGUgZ2Vub21lcywgZS5nLiBfRC4gaGFuc2VuaWlfCgpgYGB7cn0KYmcuZnJlcSREaGFuc2VuaWkgJT4lIGdncGxvdChhZXMoeCA9IHJlbExvYykpICsgCiAgZ2VvbV9kZW5zaXR5KCkgKyBmYWNldF93cmFwKH4gY2hyb21vc29tZSkgKyBnZ3RpdGxlKCJELiBoYW5zZW5paSIpICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpCmBgYAo+IFdlIGNhbiBzZWUgdGhhdCB0aGVyZSBpcyB0eXBpY2FsbHkgYSBkcm9wIGluIGdlbmUgZGVuc2l0eSB0b3dhcmRzIHRoZSBjaHJvbW9zb21lIGVuZHMsIHdoaWNoIG1ha2VzIHRoZSBvYnNlcnZhdGlvbiB0aGF0IG91ciBwcm90ZWluIGZhbWlseSBhcmUgYmlhc2VkIHRvd2FyZHMgdGhlIGNocm9tb3NvbWFsIGVuZHMgZXZlbiBtb3JlIHN0cmlraW5nLgoKXyoqTm90ZSoqIHRoYXQgdGhlIGFib3ZlIHBsb3Qgd2FzIHdyb25nIGR1ZSB0byB0aGUgd2F5IHRoZSBkZW5zaXR5KCkgZnVuY3Rpb24gZXN0aW1hdGVzIHRoZSBrZXJuZWwgZGVuc2l0eSwgd2hpY2ggYXNzdW1lcyB0aGF0IHRoZSBkYXRhIG91dHNpZGUgdGhlIHJhbmdlIGFyZSBwb3NzaWJsZSwgaW4gdGhpcyBjYXNlIHNtYWxsZXIgdGhhbiAwIGFuZCBncmVhdGVyIHRoYW4gMS4gVGhpcyBsZWFkcyB0byB0aGUgdW5kZXJlc3RpbWF0aW9uIG9mIHRoZSBkZW5zaXR5IGF0IHRoZSBib3VuZGFyaWVzIGJlY2F1c2UgaXQgaW5jbHVkZXMgcmVnaW9ucyBvdXRzaWRlIHRoZSBwb3NzaWJsZSByYW5nZSwgd2hlcmUgdGhlcmUgaXMgbm8gb2JzZXJ2YXRpb24uIFNlZSB0aGlzIHBvc3QgZm9yIGRpc2N1c3Npb24gaHR0cHM6Ly9naXRodWIuY29tL3RpZHl2ZXJzZS9nZ3Bsb3QyL2lzc3Vlcy8zMzg3XwoKQmVsb3cgSSB1c2UgdGhlIGBnZW9tX2hpc3RvZ3JhbWAgZnVuY3Rpb24gaW5zdGVhZCwgd2l0aCBicmVha3Mgc3BlY2lmaWVkIG1hbnVhbGx5LiBUaGlzIHJlc3VsdHMgaW4gYSBkZW5zaXR5IGRpc3RyaWJ1dGlvbiB0aGF0IGlzIHByZXR0eSBmbGF0IGFjcm9zcyB0aGUgY2hyb21vc29tZXMuCmBgYHtyfQpiZy5mcmVxJERoYW5zZW5paSAlPiUgZ2dwbG90KGFlcyh4ID0gcmVsTG9jKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShicmVha3MgPSBzZXEoMCwxLDAuMDUpKSArCiAgZmFjZXRfd3JhcCh+IGNocm9tb3NvbWUpICsgZ2d0aXRsZSgiRC4gaGFuc2VuaWkiKSArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKQpnZ3NhdmUoIm91dHB1dC9pbWcvMjAyMTAzMDMtZGhhbnNlbmlpLWNocm9tb3NvbWUtZ2VuZS1kZW5zaXR5LWhpc3RvZ3JhbS5wbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDQpCmBgYAoKSW4gb3JkZXIgdG8gY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9uIG9mIF9hbGxfIGdlbmVzIGluIGRpZmZlcmVudCBiaW5zIG9mIHRoZSBjaHJvbW9zb21lcyB0byB0aGUgaG9tb2xvZ3MgaW4gb3VyIGNhc2Ugc3R1ZHksIHdlIGNhbiBkaXZpZGUgZWFjaCBjaHJvbW9zb21lIGluIHRoZSBgbnJvdyh1c2Uuc3BzKWAgZ2Vub21lcyBpbnRvIGFuIGFyYml0cmFyeSBudW1iZXIgb2YgYmlucyBhZnRlciAiZm9sZGluZyIgdGhlbSBpbiBoYWxmLCBlLmcuIDAtMTAlLCAxMC0yMCUsIDIwLTMwJSwgMzAtNDAlIGFuZCA0MC01MCUuIFRvIGJlIGFibGUgdG8gdmlzdWFsbHkgY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9uIG9mIG91ciBob21vbG9ncyBhbmQgdGhlIGdlbm9tZSBiYWNrZ3JvdW5kLCB3ZSB3aWxsIGNyZWF0ZSBhIHNwZWNpYWwgImNocm9tb3NvbWUiIGNsYXNzIHRoYXQgd2lsbCBiZSBvdXIgaG9tb2xvZ3MgYW5kIGNvbWJpbmUgdGhlbSB3aXRoIHRoZSBgYmcuZnJlcWAgdGFibGUuCmBgYHtyfQpmcmVxLmJpbnMgPC0gYygtMC4wMDEsIHNlcSgwLjEsIDAuNSwgMC4xKSk7IGZyZXEuYmluc0wgPC0gYygwLCBmcmVxLmJpbnNbLTFdKQpmcmVxLmxhYmVsIDwtIHBhc3RlMChoZWFkKGZyZXEuYmluc0wsIC0xKSoxMDAsIi0iLHRhaWwoZnJlcS5iaW5zTCwgLTEpKjEwMCwiJSIpCmJnLmZyZXEudGIgPC0gYmluZF9yb3dzKGJnLmZyZXEsIC5pZCA9ICJzcGVjaWVzIikgJT4lIAogIG11dGF0ZShmb2xkLnJlbExvYyA9IGlmZWxzZShyZWxMb2MgPD0gMC41LCByZWxMb2MsIDEtcmVsTG9jKSwKICAgICAgICAgYmluID0gY3V0KGZvbGQucmVsTG9jLCBicmVha3MgPSBmcmVxLmJpbnMsIGxhYmVscyA9IGZyZXEubGFiZWwpKQoKIyBhZGQgdGhlIGhvbW9sb2dzCmZnLmZyZXEgPC0gZmcuZnJlcSAlPiUgCiAgbXV0YXRlKGZvbGQucmVsTG9jID0gaWZlbHNlKHJlbExvYyA8PSAwLjUsIHJlbExvYywgMS1yZWxMb2MpLAogICAgICAgICBiaW4gPSBjdXQoZm9sZC5yZWxMb2MsIGJyZWFrcyA9IGZyZXEuYmlucywgbGFiZWxzID0gZnJlcS5sYWJlbCkpIApmcmVxLnBsb3QgPC0gZmcuZnJlcSAlPiUKICBtdXRhdGUoY2hyb21vc29tZSA9ICJYIikgJT4lICAjIHdlIGxhYmVsIHRoZSBob21vbG9ncyBhcyBYIHRvIG1ha2UgaXQgYSBzZXBhcmF0ZSBjbGFzcwogIHNlbGVjdChzcGVjaWVzID0gc3BlY2llc19pZCwgY2hyb21vc29tZSwgYmluKSAlPiUgCiAgYmluZF9yb3dzKHNlbGVjdChiZy5mcmVxLnRiLCBzcGVjaWVzLCBjaHJvbW9zb21lLCBiaW4pKSMgJT4lIAogICNmaWx0ZXIoIXNwZWNpZXMgJWluJSBjKCJOY2FzdGVsbGlpIikpICMgcmVtb3ZlIG9uZSBzcGVjaWVzIHRvIG1ha2UgaXQgZWFzaWVyIHRvIHBsb3QKCiMgcGxvdApmcmVxLnBsb3QgJT4lIAogIG11dGF0ZShzcGVjaWVzID0gcGFzdGUwKHN1YnN0cihzcGVjaWVzLCAxLCAxKSwgIi4gIiwgc3Vic3RyKHNwZWNpZXMsIDIsIDE1KSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjaHJvbW9zb21lLCBncm91cCA9IGJpbiwgZmlsbCA9IGJpbikpICsgCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9maWxsKCkpICsKICBmYWNldF93cmFwKH4gc3BlY2llcywgc2NhbGVzID0gImZyZWVfeCIpICsgCiAgI3NjYWxlX2ZpbGxfYnJld2VyKCJEaXN0YW5jZSBmcm9tXG5jaHJvbW9zb21lIGVuZCIsIHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSAzKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIGVuZCA9IDAuOTUsIGFscGhhID0gMC45KSArCiAgc2NhbGVfeV9jb250aW51b3VzKG5hbWUgPSAiQ3VtdWxhdGl2ZSAlIG9mIGdlbmVzIiwgdHJhbnMgPSAicmV2ZXJzZSIsIGJyZWFrcyA9IHNlcSgwLDEsMC4yKSkgKyAKICB0aGVtZV9jb3dwbG90KCkgKyBwYW5lbF9ib3JkZXIoY29sb3IgPSAiZ3JleTgwIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoZmFjZSA9IDMpKQoKZ2dzYXZlKCJvdXRwdXQvaW1nLzIwMjEwMzAzLWNvbXBhcmUtaG9tb2xvZ3MtY2hyb21vc29tYWwtbG9jYXRpb25zLXRvLWJnLnBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gNCkKYGBgCkluIHRoZSBwbG90IGFib3ZlLCAiWCIgaXMgYSBzcGVjaWFsIGNhdGVnb3J5IHRoYXQgY29sbGVjdHMgb3VyIGhvbW9sb2dzLiBXZSBjYW4gc2VlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGVzZSBwcm90ZWlucyBvbiB0aGUgY2hyb21vc29tZSBkZXZpYXRlIGZyb20gdGhlIGJhY2tncm91bmQuIE5vdGUgdGhhdCBhIGZldyBvZiB0aGUgc2V2ZW4gc3BlY2llcyBjb250YWluIG9ubHkgMS0yIFBGMTE3NjUgaG9tb2xvZ3MgLS0gdGhlc2UgaW5jbHVkZSB0aGUgdGhyZWUgaW4gdGhlIG1pZGRsZSByb3cgLS0gYW5kIHdlIHNob3VsZCBpbnRlcnByZXQgdGhlaXIgaG9tb2xvZ3MgZGlzdHJpYnV0aW9uIHdpdGggY2F1dGlvbi4KCk5vdyB0aGF0IHdlIGhhdmUgdGhlIGJhY2tncm91bmQgZ2VuZSBmcmVxdWVuY2llcyBjb21wdXRlZCwgd2UgY2FuIHN0YXJ0IGNvbnN0cnVjdGluZyBhIHRlc3QgdGhhdCB0ZXN0cyBmb3Igc2lnbmlmaWNhbnQgZGVwYXJ0dXJlIGluIHRoZSBjaHJvbW9zb21hbCBsb2NhdGlvbnMgb2Ygb3VyIHByb3RlaW4gZmFtaWx5IGZyb20gdGhlIGJhY2tncm91bmQgZnJlcXVlbmNpZXMuIE9uZSBpZGVhIGlzIHRvIGRpdmlkZSB0aGUgY2hyb21vc29tZSBpbnRvIGVxdWFsLXNpemVkIGJpbnMgYW5kIHVzZSB0aGUgZnJlcXVlbmNpZXMgb2YgX2FsbF8gZ2VuZXMgaW4gZWFjaCBiaW4gYXMgdGhlIG11bHRpbm9taWFsIHByb2JhYmlsaXRpZXMgaW4gdGhlIG51bGwgaHlwb3RoZXNpcy4gSWYgb3VyIHByb3RlaW5zIHdlcmUgcmFuZG9tbHkgc2VsZWN0ZWQgZnJvbSBlYWNoIGNocm9tb3NvbWUgd2l0aG91dCByZWdhcmQgdG8gdGhlaXIgbG9jYXRpb24sIHdlIHdvdWxkIGV4cGVjdCB0aGVpciBsb2NhdGlvbnMgdG8gY29uZm9ybSB0byB0aGUgYmFja2dyb3VuZCBmcmVxdWVuY2llcy4gVGhpcyBjYW4gYmUgdGVzdGVkIHVzaW5nIGFuIGV4YWN0IG11bHRpbm9taWFsIHRlc3Qgb3IgYW4gYXBwcm94aW1hdGUgQ2hpLXNxdWFyZSB0ZXN0IG9yIEctdGVzdC4gU2luY2Ugd2UgZG9uJ3QgZGlzdGluZ3Vpc2ggdGhlIHR3byBlbmRzIG9mIGEgY2hyb21vc29tZSwgd2UgY2FuICJmb2xkIiB0aGUgY2hyb21vc29tZSAiaW4gaGFsZiIgYW5kIG1lYXN1cmUgZWFjaCBnZW5lJ3MgbG9jYXRpb24gYXMgYSBwZXJjZW50YWdlIGZyb20gdGhlIGNsb3NlciBlbmQsIGkuZS4gdGhlIHJlbGF0aXZlIGxvY2F0aW9uIHJhbmdpbmcgZnJvbSAwJS01MCUuIElmIHdlIGNhbiByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcywgdGhhdCBjb25zdGl0dXRlcyBldmlkZW5jZSB0aGF0IHRoZSBwcm90ZWlucyBmcm9tIG91ciBncm91cCBhcmUgbm90IHJhbmRvbWx5IHNlbGVjdGVkIGZyb20gdGhlIGJhY2tncm91bmQgc2V0LgoKKipSZXN1bHQgb2YgbXVsdGlub21pYWwgdGVzdCoqCgpgYGB7ciBtdWx0aW5vbWlhbF90ZXN0fQojIGluc3RhbGwgWE5vbWlhbCBwYWNrYWdlIGZvciB0aGUgbXVsdGlub21pYWwgZXhhY3QgdGVzdCBmdW5jdGlvbgojIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9YTm9taWFsL3ZpZ25ldHRlcy9YTm9taWFsLmh0bWwjZTEKd2hpbGUoIXJlcXVpcmUoWE5vbWlhbCkpCiAgc3VwcHJlc3NNZXNzYWdlcyhpbnN0YWxsLnBhY2thZ2VzKCJYTm9taWFsIikpCiMgY2FsY3VsYXRlIHBvb2xlZCBiYWNrZ3JvdW5kIGZyZXF1ZW5jeQpiZy5jbnQgPC0gY3V0KGJnLmZyZXEudGIkZm9sZC5yZWxMb2MsIGJyZWFrcyA9IGZyZXEuYmlucywgbGFiZWxzID0gZnJlcS5sYWJlbCkgJT4lIHRhYnVsYXRlKCkKZmcuY250IDwtIHRhYnVsYXRlKGZnLmZyZXEkYmluKQp4bXVsdGkob2JzID0gZmcuY250LCBleHByID0gYmcuY250LCBkZXRhaWwgPSAzKQpgYGAKTm90ZSB0aGF0IHRoZSB0aHJlZSBfUF8tdmFsdWVzIGNvcnJlc3BvbmQgdG8gdGhyZWUgYXBwcm9hY2hlcyBvZiB0ZXN0aW5nIHRoZSBnb29kbmVzcy1vZi1maXQuIFRoZSBMTFIsIHdoaWNoIHN0YW5kcyBmb3IgTG9nIExpa2VsaWhvb2QgUmF0aW8sIGlzIGdlbmVyYWxseSBwcmVmZXJyZWQuIEJ1dCBhbGwgdGhyZWUgc2FpZCB0aGUgc2FtZSB0aGluZzogdGhlIG9ic2VydmF0aW9uIGRldmlhdGVzIGZyb20gdGhlIGJhY2tncm91bmQgZnJlcXVlbmN5IHNpZ25pZmljYW50bHkuIEJ5IGxvb2tpbmcgYXQgdGhlIHBsb3RzIGFib3ZlLCBpdCBpcyBjbGVhciB0aGF0IHRoZSBkZXZpYXRpb24gaXMgZHVlIHRvIHRoZSBleGNlc3Mgb2Ygb3VyIGhvbW9sb2dzIHJlc2lkaW5nIGluIHRoZSAwLTEwJSBiaW4sIHdoaWNoIGlzIHRoZSB0aXAgb2YgdGhlIGNocm9tb3NvbWVzLgoKVGhlIHRlc3QgYWJvdmUgYXNzdW1lcyB0aGF0IHRoZSBnZW5lIGRlbnNpdHkgYWxvbmcgYWxsIGNocm9tb3NvbWVzIGluIHRoZSBzZXZlbiBzcGVjaWVzIGZvbGxvdyB0aGUgc2FtZSBkaXN0cmlidXRpb24uIFRoZSBlbXBpcmljYWwgb2JzZXJ2YXRpb24gc3VwcG9ydHMgdGhhdCBoeXBvdGhlc2lzLiBTaG91bGQgdGhhdCB0byBiZSBub3QgdGhlIGNhc2UsIHdlIGNvdWxkIGFsc28gYWNjb3VudCBmb3IgdGhlIHZhcmlhYmlsaXR5IGluIGdlbmUgZGVuc2l0eSBkaXN0cmlidXRpb24gYmV0d2VlbiBzcGVjaWVzIG9yIGV2ZW4gYmV0d2VlbiBjaHJvbW9zb21lcyB3aXRoaW4gYSBzcGVjaWVzLiBUaGUgaWRlYSBpcyB0byBmaXJzdCBjb2xsZWN0IHRoZSBjaHJvbW9zb21lcyB0aGF0IG91ciBob21vbG9ncyBjb21lIGZyb20sIGFuZCB0aGVuIHJhbmRvbWx5IHNhbXBsZSB0aGUgKipzYW1lKiogbnVtYmVyIG9mIGdlbmVzIGFzIHRoZSBudW1iZXIgb2Ygb3VyIGhvbW9sb2dzIG9uIHRoYXQgY2hyb21vc29tZS4gRG8gdGhpcyBmb3IgYWxsIG9mIHRoZSBob21vbG9nLWNvbnRhaW5pbmcgY2hyb21vc29tZXMsIHdlIHdvdWxkIG9idGFpbiBvbmUgcmFuZG9tIHNhbXBsZS4gUmVwZWF0IHRoaXMgcHJvY2VzcyAxMDAwIHRpbWVzIG9yIG1vcmUsIHdlIHdpbGwgdGhlbiBnZXQgdGhlIHBzZXVkb3NhbXBsZSwgd2hpY2ggd2UgY2FuIHVzZSB0byBjb21wYXJlIHdpdGggb3VyIG9ic2VydmF0aW9ucy4gSGVyZSB3ZSBuZWVkIHRvIGNvbWUgdXAgd2l0aCBhIHN0YXRpc3RpYyB0aGF0IHN1bW1hcml6ZXMgZWFjaCBvZiBvdXIgb2JzZXJ2YXRpb24gb3IgcHNldWRvc2FtcGxlLiBGb3IgZXhhbXBsZSwgd2UgY291bGQgY2FsY3VsYXRlIHRoZSBtZWRpYW4gJSBsb2NhdGlvbiBmb3IgZWFjaCBzYW1wbGUsIGFuZCBhc2sgd2hlcmUgb3VyIG9ic2VydmF0aW9uIGxpZSByZWxhdGl2ZSB0byB0aGUgcHNldWRvc2FtcGxlcy4gSSdsbCBza2lwIHRoaXMgYXBwcm9hY2ggZm9yIG5vdyBzaW5jZSB0aGUgZmlyc3QgYXBwcmFvY2ggYXBwZWFycyB0byBiZSBPSyBnaXZlbiB0aGUgc2ltaWxhciBnZW5lIGRlbnNpdHkgZGlzdHJpYnV0aW9uIGFjcm9zcyBjaHJvbW9zb21lcyBhbmQgc3BlY2llcy4=